使用正则表达式匹配多行文本
Posted
技术标签:
【中文标题】使用正则表达式匹配多行文本【英文标题】:Match multiline text using regular expression 【发布时间】:2011-04-08 18:43:25 【问题描述】:我正在尝试使用 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?
【问题讨论】:
【参考方案1】: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
,一个单词字符。
【讨论】:
我想匹配任何以“用户评论”开头的字符串,并且该字符串也可以包含换行符。所以我使用了User Comments: [\\s\\S]*
模式,这很有效。 (谢谢!)从@Tim 的回答中,我得到了User Comments:(.*)
的模式,这也可以现在,这些中是否有推荐 或更好 方式,或者是这只是两种方法吗?
@Nivas 我认为在性能方面不会有任何差异;但我认为(.*)
和DOTALL
标志比([\\s\\S]*)
更明显/可读
这是最好的答案....提供对 Java 代码和模式字符串选项的访问,以实现 MultiLine 功能。【参考方案2】:
首先,您在错误假设下使用了修饰符。
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:
之后的文本
【讨论】:
我正在尝试找到一个匹配任何以“用户评论:”开头的字符串的模式。在此“用户评论:”之后是用户在文本区域中输入的内容,因此可以包含 anything - 甚至是新行。看来我需要在正则表达式中学到很多东西...... 这行得通(谢谢!)我尝试了模式(?s)User Comments:\s*(.*)
。从@Amarghosh 的回答中,我得到了模式User Comments: [\\s\\S]*
。其中有一个更好的或推荐的方式,还是这只是两种不同的方式?
两者意思相同; [\s\S]
更明确一点(“匹配任何空白或非空白字符”),.
更易于阅读,但您需要查找 (?s)
或 DOTALL
修饰符才能找出是否包含换行符。我更喜欢带有Pattern.DOTALL
标志的.
(在我看来,这比(?s)
更容易阅读和记住。你应该使用你觉得最舒服的东西。
.*
和 DOTALL
更具可读性。我用另一个来表明问题在于 str.matches 和 matcher.find 之间的差异,而不是标志。 +1
我更喜欢 .*
和 Pattern.DOTALL
,但必须使用 (?s),因为我必须使用 String.matches
。【参考方案3】:
这与 MULTILINE 标志无关;您看到的是 find()
和 matches()
方法之间的区别。 find()
如果可以在目标字符串中的任意位置找到匹配项,则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 正则表达式中的“多行”和“单行”模式。再过二十年,也许世界其他地方也会效仿。
【讨论】:
很难相信他们使用方法名称“#matches”来表示“匹配所有”耶 @alan-moore 抱歉,尽管它是正确的 [需要更多睡眠 :)]【参考方案4】:多行标志告诉正则表达式将模式匹配到每一行,而不是整个字符串,为了您的目的,通配符就足够了。
【讨论】:
以上是关于使用正则表达式匹配多行文本的主要内容,如果未能解决你的问题,请参考以下文章