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);
Run Code Online (Sandbox Code Playgroud)
Ala*_*ore 138
compile()
总是在某个时刻调用该方法; 这是创建Pattern对象的唯一方法.所以问题是,你为什么要明确地称之为?一个原因是您需要对Matcher对象的引用,以便您可以使用其方法,例如group(int)
检索捕获组的内容.获取Matcher对象的唯一方法是通过Pattern对象的matcher()
方法,获取Pattern对象的唯一方法是通过该compile()
方法.然后是find()
方法,与matches()
String或Pattern类中不重复的方法不同.
另一个原因是避免反复创建相同的Pattern对象.每次使用String中的一个正则表达式方法(或matches()
Pattern中的静态方法)时,它都会创建一个新的Pattern和一个新的Matcher.所以这段代码片段:
for (String s : myStringList) {
if ( s.matches("\\d+") ) {
doSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
......完全等同于:
for (String s : myStringList) {
if ( Pattern.compile("\\d+").matcher(s).matches() ) {
doSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
显然,那是在做很多不必要的工作.事实上,编译正则表达式并实例化Pattern对象比执行实际匹配所需的时间更长.因此,将该步骤拉出循环通常是有意义的.您也可以提前创建Matcher,尽管它们并不是那么昂贵:
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("");
for (String s : myStringList) {
if ( m.reset(s).matches() ) {
doSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
如果您熟悉.NET正则表达式,您可能想知道Java的compile()
方法是否与.NET的RegexOptions.Compiled
修饰符相关; 答案是不.Java的Pattern.compile()
方法仅相当于.NET的Regex构造函数.指定Compiled
选项时:
Regex r = new Regex(@"\d+", RegexOptions.Compiled);
Run Code Online (Sandbox Code Playgroud)
......它直接编译的正则表达式CIL字节代码,允许其以更快的速度进行,但在先期处理和内存使用的显著成本-认为它是类固醇的正则表达式.Java没有等价物; 在幕后创建的模式String#matches(String)
与您明确创建的模式之间没有区别Pattern#compile(String)
.
(编辑:我原来是说,所有的.NET regex对象缓存,这是不正确由于.NET 2.0,自动缓存只有像静态方法出现Regex.Matches()
,而不是当你直接调用正则表达式的构造.REF)
Tho*_*ung 37
Compile 解析正则表达式并构建内存中表示.与匹配相比,编译的开销很大.如果你反复使用一个模式,它将获得一些缓存编译模式的性能.
jjn*_*guy 17
编译时,Pattern
Java会进行一些计算,以便String
更快地找到匹配项.(构建正则表达式的内存表示)
如果您要Pattern
多次重复使用,您会发现Pattern
每次创建新内容时性能都会大幅提升.
在仅使用Pattern一次的情况下,编译步骤似乎只是一行额外的代码,但事实上,它在一般情况下非常有用.
这与性能和内存使用情况有关,如果需要大量使用,请编译并保留编译后的模式。regex的典型用法是验证用户输入(格式),并格式化用户的输出数据,在这些类中,保存编译后的模式似乎很合逻辑,因为通常它们会经常调用它。
下面是一个示例验证器,它真的很多:)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
如@Alan Moore所述,如果您的代码中有可重用的正则表达式(例如在循环之前),则必须编译并保存模式以供重用。
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()));
}
Run Code Online (Sandbox Code Playgroud)
compileOnce 的速度提高了3 倍到 4 倍。我想这很大程度上取决于正则表达式本身,但对于经常使用的正则表达式,我选择static Pattern pattern = Pattern.compile(...)