【问题标题】:Negative lookahead regex not working in Java负前瞻正则表达式在 Java 中不起作用
【发布时间】:2015-12-27 04:39:15
【问题描述】:

以下正则表达式在测试here 时成功运行,但是当我尝试将它实现到我的 Java 代码中时,它不会返回匹配项。它使用负前瞻来确保MAIN LEVELBedrooms 之间不会出现换行符。为什么它不能在 Java 中工作?

正则表达式

^\s*\bMAIN LEVEL\b\n(?:(?!\n\n)[\s\S])*\bBedrooms:\s*(.*)

Java

pattern = Pattern.compile("^\\s*\\bMAIN LEVEL\\b\\n(?:(?!\\n\\n)[\\s\\S])*\\bBedrooms:\\s*(.*)");
    match = pattern.matcher(content);      
    if(match.find())
    {
        //Doesn't reach here
        String bed = match.group(1);
        bed = bed.trim();
    }

content 只是从文本文件中读取的字符串,其中包含上面链接的演示中显示的确切文本。

File file = new File("C:\\Users\\ME\\Desktop\\content.txt"); 
 content = new Scanner(file).useDelimiter("\\Z").next();

更新:

我更改了我的代码以包含多行修饰符 (?m),但它打印出“null”。

pattern = Pattern.compile("(?m)^\\s*\\bMAIN LEVEL\\b\\n(?:(?!\\n\\n)[\\s\\S])*\\bBedrooms:\\s*(.*)");
    match = pattern.matcher(content);
    if(match.find())
    {   // Still not reaching here
        mainBeds=match.group(1);
        mainBeds= mainBeds.trim();
    }
  System.out.println(mainBeds);     // Prints null

【问题讨论】:

  • 在开头添加多行修饰符(?m)..
  • Pattern.compile("(?m)^\\s*\\bMAIN LEVEL\\b\\n(?:(?!\\n\\n)[\\s\\S])*\\bBedrooms:\\s*(.*)");
  • 感谢@AvinashRaj,但它正在返回null。我已经更新了我的问题以反映这些变化。有什么想法吗??
  • 如果我只是使用pattern = Pattern.compile("Bedrooms:\\s(\\d+)");,那么它会正确打印666
  • match Matcher obj? Matcher match = pattern.matcher(content);

标签: java regex pattern-matching negative-lookahead


【解决方案1】:

问题:

正如Alan Moore's answer 中所述,文件中使用的Line-Separators 的格式(\r\n) 与您的模式指定的格式(\n) 不匹配:

原始代码:
Pattern.compile("^\\s*\\bMAIN LEVEL\\b\\n(?:(?!\\n\\n em>)[\\s\\S])*\\bBedrooms:\\s*(.*)");

注意:我在“旁注”部分的第二项中解释了\r\n 代表什么,以及\r\n\n 之间的上下文和区别.


解决方案:

  1. 大多数/所有 Java 版本:
    您可以使用\r?\n 来匹配这两种格式,这在大多数情况下就足够了

  2. 大多数/所有 Java 版本:
    您可以使用\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029] 来匹配“任何Unicode 换行序列”

  3. Java 8 及更高版本:
    您可以使用Linebreak Matcher (\R)。它相当于第二种方法(以上),并且只要可能(Java 8 或更高版本),这是推荐的方法

结果代码(第三种方法):
Pattern.compile("^\\s*\\bMAIN LEVEL\\b\\R(?:(?!@ 987654342@)[\\s\\S])*\\bBedrooms:\\s*(.*)");


旁注:

  1. 您可以将\\R\\R 替换为\\R{2},这样更具可读性。

  2. 存在不同格式的换行符并用于不同的系统,因为早期的操作系统从机械打字机(如打字机)继承了“换行符逻辑”。

    代码中的\r 代表回车,又名CR。这背后的想法是将打字光标返回到行首。

    代码中的\n 代表换行,又名LF。这背后的想法是将打字光标移动到下一行。

    最常见的换行格式是CR-LF (\r\n),主要由 Windows 使用;和LF (\n),被大多数类 UNIX 系统使用。这就是 \r?\n 在大多数情况下足够的原因,并且您可以可靠地将其用于面向家庭级用户的系统。

    然而,一些(罕见的)操作系统,通常用于工业级的东西,例如服务器,可能会使用 CRLF-CR 或完全其他的东西,这就是为什么第二种方法中有这么多字符的原因,所以如果你需要代码兼容每个系统,`你会需要第二种,或者最好是第三种方法。

  3. 这是测试模式失败的有用方法:

    String content = "..."; //Replace "..." with your content.
    String patternString = "..."; //Replace "..." with your pattern.
    String lastPatternSuccess = "None. You suck at Regex!";
    for (int i = 0; i <= patternString.length(); i++) {
      try {
        String patternSubstring = patternString.substring(0, i);
        Pattern pattern = Pattern.compile(patternSubstring);
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
          lastPatternSuccess = i + " - Pattern: " + patternSubstring + " - Match: \n" + matcher.group();
        }
      } catch (Exception ex) {
        //Ignore and jump to next
      }
    }
    System.out.println(lastPatternSuccess);
    

【讨论】:

  • 好的,这确实改善了我的回答,而且故障排除的噱头非常可爱!
  • 谢谢,我回家看看再报告。
  • @AlanMoore 谢谢! =)
  • @AlmightyR,我实际上已经意识到\r\n\n 的这个要求,具体取决于系统,但昨晚我完全没有意识到这一点。无论如何,您的回答帮助我理解了这种差异的原因,所以谢谢。我最终使用了\\R\\R{2}。快速提问:我必须从行首删除^,以便正则表达式找到匹配项。这很奇怪,因为我直接从打印的content 复制了演示文本。由于以下代码使用分隔符,是否需要删除 ^content = new Scanner(file).useDelimiter("\\Z").next();谢谢
  • @JimJim That might be the case: "默认情况下,正则表达式^$ 忽略行终止符,仅分别匹配整个输入的开头和结尾序列。如果激活MULTILINE 模式,则^ 匹配输入开头和除输入结尾之外的任何行终止符之后。当处于MULTILINEmode 时,$ 匹配行终止符之前或结尾输入序列。”
【解决方案2】:

这是行分隔符。您正在寻找\n,但您的文件实际上使用了\r\n。如果您运行的是 Java 8,则可以将代码中的每个 \\n 更改为 \\R(通用行分隔符)。对于 Java 7 或更早版本,请使用 \\r?\\n

【讨论】:

  • 您需要将 all 设为行分隔符 \r\n 才能看到我在说什么。正则表达式中的第一个 \n 无法匹配第一行分隔符(在 MAIN LEVEL 之后),如果您修复它,负前瞻 ((?!\\n\\n)) 会错误地匹配部分之间的空白行,从而导致正则表达式匹配整个字符串。
  • 删除了过时的 cmets。回答以清晰、简洁的方式解释问题和解决方案; +1。但我认为我可以做得更好,所以我会无耻地复制和改进(有应有的功劳)。
  • 是的,这是艾伦的问题。谢谢你。但是(如上面的评论中所解释的),我必须从我的 Java 代码中删除前导插入符号 ^ 才能运行,尽管上面正常运行的演示文本是从代码的 content 字符串中复制的。我想知道这是否与在检索文件后使用分隔符有关:File file = new File("C:\\Users\\ME\\Desktop\\content.txt"); content = new Scanner(file).useDelimiter("\\Z").next(); Thoughts?
猜你喜欢
  • 2011-07-21
  • 1970-01-01
  • 2017-04-22
  • 2012-06-22
  • 2012-12-07
  • 2019-04-05
  • 2021-10-11
  • 2011-10-14
  • 2010-12-17
相关资源
最近更新 更多