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

2022-08-31 06:29:31

我正在尝试使用java匹配多行文本。当我将类与修饰符一起使用时,我能够匹配,但我无法使用PatternPattern.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?

答案 1

首先,您在不正确的假设下使用了修饰符。

Pattern.MULTILINE或者告诉Java接受锚点并在每行的开头和结尾匹配(否则它们只在整个字符串的开头/结尾匹配)。(?m)^$

Pattern.DOTALL或者告诉Java也允许点与换行符匹配。(?s)

其次,在你的例子中,正则表达式失败了,因为你使用的方法期望正则表达式匹配整个字符串 - 这当然不起作用,因为在匹配之后还有一些字符。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:


答案 2

这与多行标志无关;你所看到的是 和 方法之间的区别。 如果可以在目标字符串中的任何位置找到匹配项,则成功,同时期望正则表达式与整个字符串匹配。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

此外,这并不意味着你认为它做了什么。许多人似乎得出这样的结论:如果您的目标字符串包含换行符(即,如果它包含多个逻辑行),则必须使用该标志。我已经在SO上看到了几个这样的答案,但实际上,该标志所做的只是改变锚点的行为,并且.MULTILINE^$

通常匹配目标字符串的开头,并匹配结尾(或在末尾的换行符之前,但现在我们将把它放在一边)。但是,如果字符串包含换行符,则可以通过设置 MULTILINE 标志,在任何逻辑行的开头和结尾(而不仅仅是整个字符串的开头和结尾)选择匹配和匹配。^$^$

因此,忘记意味着什么,只记住它的作用:改变和锚点的行为。 模式最初被称为“单行”(并且仍然在某些版本中,包括Perl和.NET),并且它总是引起类似的混淆。我们很幸运,在这种情况下,Java开发人员使用了更具描述性的名称,但是“多行”模式没有合理的替代方案。MULTILINE^$DOTALL

在 Perl 中,所有这些疯狂开始的地方,他们已经承认了自己的错误,并摆脱了 Perl 6 正则表达式中的“多行”和“单行”模式。再过二十年,也许世界其他国家也会效仿。