使用正则表达式 select 文本的特定部分

Using Regex to select specific section of a text

假设我有以下文件:

document1 = '1. Hello world\n1.1 bla bla bla\n1.2 more bla bla\n1.3 even more bla bla ABC\n\n2. ABC \n2.1 hello ABC\n2.2 bla bla bla\n\n3. XYZ\n3.1 bla bla\n3.2 more bla bla\n3.3 even more bla bla'

格式如下:

1. Hello world
1.1 bla bla bla
1.2 more bla bla
1.3 even more bla bla ABC

2. ABC 
2.1 hello ABC
2.2 bla bla bla

3. XYZ
3.1 bla bla
3.2 more bla bla
3.3 even more bla bla

我想知道如何 select ABC section only,这样我得到的输出为:

2. ABC 
2.1 hello ABC
2.2 bla bla bla

有人可能会建议做 re.findall(r'^2\..*', document1, re.MULTILINE) 注意 ABC section 并不总是必须在第 2 点。例如我可以:

document2 = '1. Hello world\n1.1 bla bla bla\n1.2 more bla bla\n1.3 even more bla bla ABC\n\n2. XYZ\n2.1 bla bla\n2.2 more bla bla\n2.3 even more bla bla\n\n\n3. MNO\n 3.1 hello MNO\n3.2 bla bla bla\n\n\n4. ABC\n4.1 hello ABC\n4.2 bla bla bla'

1. Hello world
1.1 bla bla bla
1.2 more bla bla
1.3 even more bla bla ABC

2. XYZ
2.1 bla bla
2.2 more bla bla
2.3 even more bla bla

3. MNO 
3.1 hello MNO
3.2 bla bla bla

4. ABC 
4.1 hello ABC
4.2 bla bla bla

其中 ABC 在第 4 节中。

您可以使用

^\d+\.\s*ABC[^\S\n]*(?:\n.+)*

regex demo。编译 regex 对象时只传递 re.M 标志。 详情:

  • ^ - 行首
  • \d+ - 一位或多位数字
  • \. - 一个点
  • \s* - 零个或多个空格
  • ABC - ABC 字符串
  • [^\S\n]* - 除了 LF 字符
  • 之外的零个或多个空格
  • (?:\n.+)* - 零个或多个非空行。

要获取所有匹配项,您可以使用

matches =  re.findall(r'^\d+\.\s*ABC[^\S\n]*(?:\n.+)*', document1, re.M)

要获得第一个匹配项,您可以使用

match = re.search(r'^\d+\.\s*ABC[^\S\n]*(?:\n.+)*', document1, re.M)
if match:
    print(match.group())

我会将文本分成几个段落:

>>> document1.split("\n\n")
[
  "1. Hello world\n1.1 bla bla bla\n1.2 more bla bla\n1.3 even more bla bla ABC",
  "2. ABC \n2.1 hello ABC\n2.2 bla bla bla",
  "3. XYZ\n3.1 bla bla\n3.2 more bla bla\n3.3 even more bla bla"
]

>>> document2.split("\n\n")
[
  "1. Hello world\n1.1 bla bla bla\n1.2 more bla bla\n1.3 even more bla bla ABC",
  "2. XYZ\n2.1 bla bla\n2.2 more bla bla\n2.3 even more bla bla",
  "\n3. MNO\n 3.1 hello MNO\n3.2 bla bla bla",
  "\n4. ABC\n4.1 hello ABC\n4.2 bla bla bla"
]

然后搜索包含“.ABC”的段落:

found = next((para for para in document1.split("\n\n") if ". ABC" in para), "")

以上内容也适用于 document2。如果需要,可以将测试 ". ABC" in para 替换为 re.search(r"\d+\. ABC", para).

这是获取它的一种方法,首先提取该部分的初始数字,然后应用您建议的 findall 方法。请注意,如果该部分出现多次,则需要调整代码。

import re

document1 = '1. Hello world\n1.1 bla bla bla\n1.2 more bla bla\n1.3 even more bla bla ABC\n\n2. ABC \n2.1 hello ABC\n2.2 bla bla bla\n\n3. XYZ\n3.1 bla bla\n3.2 more bla bla\n3.3 even more bla bla'
document2 = '1. Hello world\n1.1 bla bla bla\n1.2 more bla bla\n1.3 even more bla bla ABC\n\n2. XYZ\n2.1 bla bla\n2.2 more bla bla\n2.3 even more bla bla\n\n\n3. MNO\n 3.1 hello MNO\n3.2 bla bla bla\n\n\n4. ABC\n4.1 hello ABC\n4.2 bla bla bla'

def get_section(document, substr):
    section_expr = "\d*\. " + substr
    section_no = re.findall(section_expr, document)[0].rsplit('. ', 1)[0]
    subsection_expr = str(section_no) + '\..*'
    return re.findall(subsection_expr, document)

print(get_section(document1, "ABC"))
print(get_section(document2, "ABC"))

结果:

['2. ABC ', '2.1 hello ABC', '2.2 bla bla bla']
['4. ABC', '4.1 hello ABC', '4.2 bla bla bla']