繁体   English   中英

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

[英]java.util.regex - importance of Pattern.compile()?

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);

始终在某个时刻调用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)与使用Pattern#compile(String)显式创建的Pattern之间没有区别。

(编辑:我原来是说,所有的.NET regex对象缓存,这是不正确由于.NET 2.0,自动缓存只发生与像静态方法Regex.Matches()而不是当你直接调用正则表达式的构造。 REF

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

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

如果您要多次重复使用Pattern ,那么每次创建新Pattern时都会看到很大的性能提升。

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

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

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

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所提到的,如果你的代码中有可重用的正则表达式(例如在循环之前),你必须编译并保存模式以便重用。

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

与'Pattern.compile'类似,有'RECompiler.compile'[来自com.sun.org.apache.regexp.internal],其中:
1.模式[az]的编译代码中包含'az'
2.模式[0-9]的编译代码中包含'09'
3.模式[abc]的编译代码中包含'aabbcc'。

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

Pattern类是正则表达式引擎的入口点。您可以通过Pattern.matches()和Pattern.comiple()使用它。 #这两者之间的差异。 matches() - 用于快速检查文本(String)是否与给定的正则表达式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()+ " ");
     }
}

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(...)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM