有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我正在尝试使用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) 个答案

  1. # 1 楼答案

    str.matches(regex)behaves like{}它尝试根据模式匹配整个输入序列并返回

    true if, and only if, the entire input sequence matches this matcher's pattern

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

    true if, and only if, a subsequence of the input sequence matches this matcher's pattern

    因此,问题在于正则表达式。尝试以下方法

    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,一个单词字符

  2. # 2 楼答案

    这与多行标志无关;您看到的是find()matches()方法之间的差异^如果在目标字符串中的任何位置都能找到匹配项,{}将成功,而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的意思,只记得它做了什么:改变^$锚的行为DOTALL模式最初被称为“单线模式”(在某些风格中,包括Perl和.NET),它总是引起类似的混淆。幸运的是,在这种情况下,Java开发人员使用了更具描述性的名称,但“多行”模式没有合理的替代方案

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

  3. # 3 楼答案

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

  4. # 4 楼答案

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

    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:之后包含文本