负向前瞻正则表达式在 Java 中不起作用

Mat*_*tic 1 java regex pattern-matching regex-lookarounds

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

正则表达式

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

爪哇

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();
    }
Run Code Online (Sandbox Code Playgroud)

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

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

更新:

我更改了代码以包含多行修饰符(?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
Run Code Online (Sandbox Code Playgroud)

Xen*_*oRo 5

问题:

正如Alan Moore 的回答Line-Separators中所解释的,这是文件中使用的格式( \r\n) 与模式指定的格式 ( \n) 之间不匹配:

原始代码:
Pattern.compile("^\\s*\\bMAIN LEVEL\\b\\n(?:(?!\\n\\n)[\\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 及更高版本:
    您可以使用换行匹配器 ( \R)。它相当于第二种方法(上面),只要有可能(Java 8 或更高版本),这是推荐的方法

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


旁注:

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

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

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

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

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

    然而,一些(罕见的)操作系统,通常是工业级的东西,例如服务器,可能会使用CR,,LF-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);
    
    Run Code Online (Sandbox Code Playgroud)