[英]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.