问题描述
作为更简单的StackOverflow
question的扩展,有一个Java
正则表达式可以一次性从多行文本文档中提取每个节和小节,其结构类似于
<Irrelevant line>
...
<Irrelevant line>
####<section_title>
OVERVIEW
...
...
INTRODUCTION
...
...
DETAILS
...
...
####<section_title>
OVERVIEW
...
...
INTRODUCTION
...
...
DETAILS
...
...
section_title
可以是任何东西,并且每个小节标题(概述,简介,详细信息)都是该行中唯一的文本。所有其他行都可以多行包含任何文本,从空到数千个字符。
当然,当然也可以使用BufferedReader
处理文档并逐行读取,但是使用正则表达式将提供更优雅的解决方案。
解决方法
以下正则表达式在迭代时将一次返回一个子节,可选地包括第一个子节的节头。
(?m)(?:^####(.*)\R)?^(OVERVIEW|INTRODUCTION|DETAILS)\R(?s:(.*?))(?=^####|^(?:OVERVIEW|INTRODUCTION|DETAILS)$|\z)
(?m)
意味着^
和$
在正则表达式的其余部分分别匹配行的开始和结尾,因此我们使用\z
来匹配输入,即$
通常匹配的内容。
(?s:XXX)
使.
与XXX
模式的任何字符匹配,包括行分隔符(\r
,\n
)
\R
匹配\r
,\n
或\r\n
,即匹配行分隔符,而不考虑操作系统(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: ...
...