我正在尝试使用java匹配多行文本。当我将Pattern
类与Pattern.MULTILINE
修饰符一起使用时,我能够匹配,但我无法使用(?m).
使用(?m)
并使用String.matches
的相同模式似乎不起作用。
我确信我错过了什么,但不知道是什么。我不太擅长正则表达式。
这就是我试过的
String test = "User Comments: This is \t a\ta \n test \n\n message \n";
String pattern1 = "User Comments: (\\W)*(\\S)*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find()); //true
String pattern2 = "(?m)User Comments: (\\W)*(\\S)*";
System.out.println(test.matches(pattern2)); //false - why?
答案 0 :(得分:267)
首先,您在不正确的假设下使用修饰符。
Pattern.MULTILINE
或(?m)
告诉Java接受锚点^
和$
以匹配每行的开头和结尾(否则它们仅匹配开头/整个字符串的结尾)。
Pattern.DOTALL
或(?s)
告诉Java允许点匹配换行符。
其次,在你的情况下,正则表达式失败是因为你正在使用matches()
方法,它希望正则表达式匹配整个字符串 - 这当然不起作用,因为那里是(\\W)*(\\S)*
匹配后留下的一些字符。
因此,如果您只是寻找以User Comments:
开头的字符串,请使用正则表达式
^\s*User Comments:\s*(.*)
使用Pattern.DOTALL
选项:
Pattern regex = Pattern.compile("^\\s*User Comments:\\s+(.*)", Pattern.DOTALL);
Matcher regexMatcher = regex.matcher(subjectString);
if (regexMatcher.find()) {
ResultString = regexMatcher.group(1);
}
然后 ResultString
将包含User Comments:
答案 1 :(得分:37)
这与MULTILINE标志无关;您所看到的是find()
和matches()
方法之间的差异。如果可以在目标字符串中的任何位置找到匹配项,则find()
成功,而matches()
期望正则表达式匹配整个字符串。
Pattern p = Pattern.compile("xyz");
Matcher m = p.matcher("123xyzabc");
System.out.println(m.find()); // true
System.out.println(m.matches()); // false
Matcher m = p.matcher("xyz");
System.out.println(m.matches()); // true
此外,MULTILINE
并不代表您的想法。如果你的目标字符串包含换行符 - 也就是说,如果它包含多个逻辑行,那么许多人似乎都会得出结论,你必须使用该标志。我已经在这里看到了几个关于这个效果的答案,但事实上,所有那个标志都会改变锚点^
和$
的行为。
通常^
匹配目标字符串的最开头,而$
匹配最后一个(或者在结尾处的换行符之前,但我们暂时将它留在一边)。但是如果字符串包含换行符,则可以选择^
和$
来匹配任何逻辑行的开头和结尾,而不仅仅是整个字符串的开头和结尾,通过设置MULTILINE标志
请忘记MULTILINE
的含义并记住它的作用:更改^
和$
的行为锚。 DOTALL
模式最初被称为“单行”(并且仍然有一些版本,包括Perl和.NET),并且它总是引起类似的混淆。我们很幸运,Java开发人员在这种情况下采用了更具描述性的名称,但“多线”模式没有合理的替代方案。
在Perl,所有这些疯狂开始的地方,他们承认了他们的错误并且在Perl 6 regex中摆脱了“多线”和“单线”模式。再过二十年,也许世界其他地方也会效仿。
答案 2 :(得分:19)
str.matches(regex)
behaves like Pattern.matches(regex, str)
尝试将整个输入序列与模式匹配并返回
true
当且仅当整个输入序列与此匹配器的模式匹配时
而matcher.find()
attempts to find输入序列的下一个子序列与模式匹配并返回
true
当且仅当输入序列的子序列与此匹配器的模式匹配时
因此问题在于正则表达式。请尝试以下方法。
String test = "User Comments: This is \t a\ta \ntest\n\n message \n";
String pattern1 = "User Comments: [\\s\\S]*^test$[\\s\\S]*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find()); //true
String pattern2 = "(?m)User Comments: [\\s\\S]*^test$[\\s\\S]*";
System.out.println(test.matches(pattern2)); //true
因此,简而言之,第一个正则表达式中的(\\W)*(\\S)*
部分与空字符串匹配,因为*
表示零次或多次出现,而实际匹配的字符串为User Comments:
,而不是整个字符串为你期待的。第二个失败,因为它尝试匹配整个字符串,但它不能与\\W
匹配非单词字符,即[^a-zA-Z0-9_]
,第一个字符是T
,一个单词字符。
答案 3 :(得分:0)
出于您的目的,多行标志告诉正则表达式将模式匹配到每一行而不是整个字符串,通配符就足够了。