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);
答案 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(...)