java.util.regex - Pattern.compile()的重要性?

时间:2009-11-12 05:55:43

标签: java regex

Pattern.compile()方法的重要性是什么? 为什么我需要在获取Matcher对象之前编译正则表达式字符串?

例如:

String regex = "((\\S+)\\s*some\\s*";

Pattern pattern = Pattern.compile(regex); // why do I need to compile
Matcher matcher = pattern.matcher(text);

8 个答案:

答案 0 :(得分:134)

compile()方法总是在某个时候被调用;这是创建Pattern对象的唯一方法。所以问题是,你为什么要称之为明确?一个原因是您需要对Matcher对象的引用,以便您可以使用其方法(如group(int))来检索捕获组的内容。获取Matcher对象的唯一方法是通过Pattern对象的matcher()方法,获取Pattern对象的唯一方法是通过compile()方法。然后是find()方法,与matches()不同,它不会在String或Pattern类中重复。

另一个原因是避免反复创建相同的Pattern对象。每次使用String中的一个正则表达式方法(或Pattern中的静态matches()方法)时,它都会创建一个新的Pattern和一个新的Matcher。所以这段代码:

for (String s : myStringList) {
    if ( s.matches("\\d+") ) {
        doSomething();
    }
}

......完全等同于:

for (String s : myStringList) {
    if ( Pattern.compile("\\d+").matcher(s).matches() ) {
        doSomething();
    }
}

显然,那是在做很多不必要的工作。事实上,编译正则表达式并实例化Pattern对象比执行实际匹配所需的时间更长。因此,将该步骤拉出循环通常是有意义的。你也可以提前创建Matcher,虽然它们并不是那么昂贵:

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("");
for (String s : myStringList) {
    if ( m.reset(s).matches() ) {
        doSomething();
    }
}

如果您熟悉.NET正则表达式,您可能想知道Java的compile()方法是否与.NET的RegexOptions.Compiled修饰符相关;答案是不。 Java的Pattern.compile()方法仅相当于.NET的Regex构造函数。指定Compiled选项时:

Regex r = new Regex(@"\d+", RegexOptions.Compiled); 

...它将正则表达式直接编译为CIL字节代码,使其执行速度更快,但在前期处理和内存使用方面成本很高 - 将其视为正则表达式的类固醇。 Java没有等价物;在String#matches(String)幕后创建的模式与使用Pattern#compile(String)明确创建的模式之间没有区别。

(编辑:我原来说所有的.NET Regex对象都是缓存的,这是不正确的。从.NET 2.0开始,只有静态方法(如Regex.Matches())才会发生自动缓存,而不是直接调用Regex构造函数时。 ref

答案 1 :(得分:35)

编译解析正则表达式并构建内存中表示。与匹配相比,编译的开销很大。如果您使用的模式重复,它将获得一些缓存已编译模式的性能。

答案 2 :(得分:17)

编译Pattern时,Java会进行一些计算,以便更快地找到String中的匹配项。 (构建正则表达式的内存表示)

如果您要多次重复使用Pattern,那么每次创建新的Pattern都会带来巨大的性能提升。

在仅使用Pattern一次的情况下,编译步骤似乎只是一行额外的代码,但事实上,它在一般情况下非常有用。

答案 3 :(得分:5)

这是性能和内存使用的问题,如果你需要经常使用它,编译并保持编译模式。 正则表达式的典型用法是验证用户输入(格式),以及用户格式输出数据,在这些类中保存已编译的模式,看起来很合乎逻辑通常被称为很多。

下面是一个示例验证器,它实际上被称为很多:)

public class AmountValidator {
    //Accept 123 - 123,456 - 123,345.34
    private static final String AMOUNT_REGEX="\\d{1,3}(,\\d{3})*(\\.\\d{1,4})?|\\.\\d{1,4}";
    //Compile and save the pattern  
    private static final Pattern AMOUNT_PATTERN = Pattern.compile(AMOUNT_REGEX);


    public boolean validate(String amount){

         if (!AMOUNT_PATTERN.matcher(amount).matches()) {
            return false;
         }    
        return true;
    }    
}

正如@Alan Moore所提到的,如果您的代码中有可重用的正则表达式(例如循环之前),则必须编译并保存模式以供重用。

答案 4 :(得分:0)

预编译正则表达式可以提高速度。重新使用匹配器可以为您带来另一个轻微的加速。如果方法被频繁调用,则说在循环中调用,整体性能肯定会上升。

答案 5 :(得分:0)

类似于' Pattern.compile'有' RECompiler.compile' [来自com.sun.org.apache.regexp.internal]其中:
1。模式[a-z]的编译代码具有' az'在里面
2。模式[0-9]的编译代码有' 09'在里面
3。模式[abc]的编译代码有' aabbcc'在它。

因此,编译代码是概括多个案例的好方法。因此,而不是具有不同的代码处理情况1,2和3。与编译代码中的当前和下一个元素的ascii相比,问题减少了,因此成对。 从而
一个。 a和z之间ascii的任何东西都在a和z之间
湾ascii在&a; a和a之间的任何东西绝对是' a'

答案 6 :(得分:0)

Pattern类是正则表达式引擎的入口点。您可以通过Pattern.matches()和Pattern.comiple()使用它。   #两者之间的差异。 matches()-用于快速检查文本(字符串)是否与给定的正则表达式匹配 comiple()-创建Pattern的引用。因此可以多次使用以使正则表达式与多个文本匹配。

供参考:

public static void main(String[] args) {
     //single time uses
     String text="The Moon is far away from the Earth";
     String pattern = ".*is.*";
     boolean matches=Pattern.matches(pattern,text);
     System.out.println("Matches::"+matches);

    //multiple time uses
     Pattern p= Pattern.compile("ab");
     Matcher  m=p.matcher("abaaaba");
     while(m.find()) {
         System.out.println(m.start()+ " ");
     }
}

答案 7 :(得分:0)

Pattern.compile()允许多次重用正则表达式(它是线程安全的)。性能优势可能非常显着。

我做了一个快速基准测试:

    @Test
    public void recompile() {
        var before = Instant.now();
        for (int i = 0; i < 1_000_000; i++) {
            Pattern.compile("ab").matcher("abcde").matches();
        }
        System.out.println("recompile " + Duration.between(before, Instant.now()));
    }

    @Test
    public void compileOnce() {
        var pattern = Pattern.compile("ab");
        var before = Instant.now();
        for (int i = 0; i < 1_000_000; i++) {
            pattern.matcher("abcde").matches();
        }
        System.out.println("compile once " + Duration.between(before, Instant.now()));
    }

compileOnce的速度在 3倍至4倍之间。 我猜这很大程度上取决于正则表达式本身,但是对于经常使用的正则表达式,我选择了static Pattern pattern = Pattern.compile(...)