使用正则表达式匹配多行文本

时间:2010-09-06 13:14:15

标签: java regex

我正在尝试使用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?

4 个答案:

答案 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)

出于您的目的,多行标志告诉正则表达式将模式匹配到每一行而不是整个字符串,通配符就足够了。