代码之家  ›  专栏  ›  技术社区  ›  Nivas

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

  •  151
  • Nivas  · 技术社区  · 14 年前

    我尝试使用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 回复  |  直到 6 年前
        1
  •  264
  •   Tim Pietzcker    14 年前

    首先,在错误的假设下使用修饰符。

    Pattern.MULTILINE (?m) 告诉Java接受锚 ^ $ 在每行的开始和结束处匹配(否则,它们只在整个字符串的开始/结束处匹配)。

    Pattern.DOTALL (?s) 告诉Java允许点与新行字符匹配。

    其次,在您的情况下,regex失败,因为您正在使用 matches() 方法,该方法期望regex与 整个的 字符串-当然不起作用,因为后面还有一些字符 (\\W)*(\\S)* 已匹配。

    所以如果你只是在寻找一个以 User Comments: 使用正则表达式

    ^\s*User Comments:\s*(.*)
    

    图高 选项:

    Pattern regex = Pattern.compile("^\\s*User Comments:\\s+(.*)", Pattern.DOTALL);
    Matcher regexMatcher = regex.matcher(subjectString);
    if (regexMatcher.find()) {
        ResultString = regexMatcher.group(1);
    } 
    

    ResultString 之后将包含文本 用户评论:

        2
  •  37
  •   Geoffrey Zheng    11 年前

    这与多行标志无关;您看到的是 find() matches() 方法。 查找() 如果找到匹配项,则成功 目标字符串中的任何位置 ,同时 匹配() 期望regex匹配 整个字符串 .

    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 并不代表你认为它能做什么。许多人似乎会得出这样的结论:如果目标字符串包含换行符,即如果它包含多个逻辑行,则必须使用该标志。我在这里看到了几个这样的答案,但事实上,旗子所做的只是改变锚的行为, ^ $ .

    正常地 ^ 匹配目标字符串的最开始部分,并且 $ 匹配最后一个(或者在最后一个换行符之前,但我们暂时将其放在一边)。但如果字符串包含换行符,则可以选择 ^ $ 通过设置多行标志,在任何逻辑行的开始和结束处进行匹配,而不仅仅是整个字符串的开始和结束处。

    所以忘了什么吧 多线 方法 记住它是什么 :更改 ^ $ 锚。 DOTALL 模式最初被称为“单行”(仍然有一些风格,包括Perl和.NET),它总是引起类似的混乱。幸运的是,在这种情况下,Java DEVS使用了更具描述性的名称,但是对于“多行”模式没有合理的选择。

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

        3
  •  19
  •   Amarghosh    14 年前

    str.matches(regex) behaves like Pattern.matches(regex, str) 它尝试将整个输入序列与模式匹配并返回

    true 如果,而且只有当 整个的 输入序列与此匹配器的模式匹配

    反之 matcher.find() attempts to find 与模式匹配并返回的输入序列的下一个子序列

    如果且仅当 子序列 输入序列与匹配器的模式匹配

    因此,问题出在regex上。尝试以下操作。

    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)* 第一个regex中的部分与空字符串匹配为 * 表示零次或多次出现,实际匹配字符串为 User Comments: 而不是你所期望的整个字符串。第二个失败,因为它试图匹配整个字符串,但它不能 \\W 与非文字字符匹配. [^a-zA-Z0-9_] 第一个字符是 T 一个单词字符。

        4
  •  0
  •   Yehuda Schwartz    6 年前

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