Java 正则表达式匹配多行部分和小节

Java regex to match multiline sections with subsections

作为更简单的 Whosebug question 的扩展,是否有一个 Java 正则表达式可以一次从多行文本文档中提取每个部分和小节,具有结构喜欢

<Irrelevant line>
...
<Irrelevant line>
####<section_title>
OVERVIEW
...
...
INTRODUCTION
...
...
DETAILS
...
...
####<section_title>
OVERVIEW
...
...
INTRODUCTION
...
...
DETAILS
...
...

section_title 可以是任何内容,它以及每个小节标题(概述、简介、详细信息)是该行中唯一的文本。所有其他行可以在多行中包含从空到数千个字符的任何文本。

或者,当然,可以使用 BufferedReader 处理文档并逐行阅读,但正则表达式会提供更优雅的解决方案。

以下正则表达式在迭代时将 return 一次 sub-section,可选择包括第一个 sub-section.

部分 header
(?m)(?:^####(.*)\R)?^(OVERVIEW|INTRODUCTION|DETAILS)\R(?s:(.*?))(?=^####|^(?:OVERVIEW|INTRODUCTION|DETAILS)$|\z)

(?m) 表示 ^$ 匹配正则表达式其余部分的行首和行尾(分别),因此我们使用 \z 来匹配输入结束,这是 $ 通常匹配的内容。

(?s:XXX) 使 . 匹配 任何 具有 XXX 模式的字符,包括行分隔符(\r\n).

\R 匹配 \r\n\r\n,即不管 OS(Windows 与Linux).

使用 .*?(不情愿)匹配后接 (?=XXX) 将使正则表达式匹配文本,但不包括 XXX 模式。

演示
(在 regex101.com 上也可用)

String regex = "(?m)(?:^####(.*)\R)?^(OVERVIEW|INTRODUCTION|DETAILS)\R(?s:(.*?))(?=^####|^(?:OVERVIEW|INTRODUCTION|DETAILS)$|\z)";

String input = "<Irrelevant line>\r\n" + 
               "...\r\n" + 
               "<Irrelevant line>\r\n" + 
               "####<section_title>\r\n" + 
               "OVERVIEW\r\n" + 
               "...\r\n" + 
               "...\r\n" + 
               "INTRODUCTION\r\n" + 
               "...\r\n" + 
               "...\r\n" + 
               "DETAILS\r\n" + 
               "...\r\n" + 
               "...\r\n" + 
               "####<section_title>\r\n" + 
               "OVERVIEW\r\n" + 
               "...\r\n" + 
               "...\r\n" + 
               "INTRODUCTION\r\n" + 
               "...\r\n" + 
               "...\r\n" + 
               "DETAILS\r\n" + 
               "...\r\n" + 
               "...";

for (Matcher m = Pattern.compile(regex).matcher(input); m.find(); ) {
    String sectionTitle = m.group(1);
    String subSectionTitle = m.group(2);
    String content = m.group(3);
    if (sectionTitle != null)
        System.out.println("sectionTitle: " + sectionTitle);
    System.out.println("subSectionTitle: " + subSectionTitle);
    System.out.println("content: " + content.replaceAll("(?ms)(?<=.)^", "         "));
}

输出

sectionTitle: <section_title>
subSectionTitle: OVERVIEW
content: ...
         ...

subSectionTitle: INTRODUCTION
content: ...
         ...

subSectionTitle: DETAILS
content: ...
         ...

sectionTitle: <section_title>
subSectionTitle: OVERVIEW
content: ...
         ...

subSectionTitle: INTRODUCTION
content: ...
         ...

subSectionTitle: DETAILS
content: ...
         ...