首页 > 解决方案 > 使用正则表达式提取问题和答案

问题描述

我想从我正在阅读的一些文件中提取一些问题和答案,但我的正则表达式对我不起作用:

from re import findall,DOTALL

text='''
category 1
1. question
a) answer
b) answer
2. question
a) answer
b) answer

category 2
3. question
a) answer
b) answer
'''

文件中的格式基本上是一个编号列表,其中包含可变数量的索引答案,例如a) b)a. b....,答案在不同的地方跨越几行。我试过这个:

mo=findall(r"^\d\.(.+)(\w\)|\.(.+))+$",text,DOTALL)
print(mo)

我尝试放入捕获组以将问题与答案分开,删除"^"给出了最接近的结果,但它仍然是垃圾,我不明白为什么会发生这种情况:

[(' question\na) answer\nb) answer\n2. question\na) answer\nb) answer\ncategory 2\n3', '. question\na) answer\nb) answer\n', ' question\na) answer\nb) answer\n')]

我正在考虑在答案之间寻找一个空格,以免将“类别”垃圾作为答案的一部分,或者更多地控制我的输入以支持没有空格的格式。

我正在尝试获得类似的输出(不需要是元组,这正是 findall 组返回的内容):

[('question', 'answer', 'answer'), 
 ('question', 'answer', 'answer'), 
 ('question', 'answer', 'answer')]

标签: pythonregex

解决方案


我不会为多行需求编写怪物正则表达式,而是或多或少地使用正常的迭代和累积。您可以拆分/\n(?=[a-z\d][).] )/gm以仅提取问答内容。遍历这些块,如果有问题,则开始一个新的问答块,否则附加到现有的块以累积结果。

import re

text = '''
category 1
1. q1
  q1 foobar
a) a1.a
b) a1.b
  some extra a1.b
2. q2
a) a2.a
b) a2.b
  some extra a2.b
c) a2.c
 blah a2.c

category 2
3. q3
a) a3.a
b) a3.b
extra a3.b
'''

qa = []
block = []

for chunk in re.split(r"\n(?=[a-z\d][).] )", text):
    if m:= re.match(r"\d+\. (.+)", chunk, re.S):
        qa.append(tuple(block))
        block = [m.group(1)]
    elif m := re.match(r"[a-z]+\) (.+?)(?=\n\n|$|[a-z]+\) )", chunk, re.S):
        block.append(m.group(1))

qa = qa[1:] + [tuple(block)]

for line in qa: 
    print(line)

给出:

('q1\n  q1 foobar', 'a1.a', 'a1.b\n  some extra a1.b')
('q2', 'a2.a', 'a2.b\n  some extra a2.b', 'a2.c\n blah a2.c')
('q3', 'a3.a', 'a3.b\nextra a3.b')

正则表达式解释:

  • /\n(?=[a-z\d][).] )/gs在前瞻两个a)1.模式的换行符上进行拆分。这使我们能够保留多行块。
  • /\d+\. (.+)/gs让我们识别一个1. question块并捕获问题主体。
  • /[a-z]+\) (.+?)(?=\n\n|$|[a-z]+\) )/gs匹配a) answer块。它与上面的块几乎相同1. question,但它必须小心修剪下一个内容标题,它不是由上面的正则表达式 (1) 处理的。这就是(?=\n\n|$|[a-z]+\) )前瞻的作用:如果以下是双换行符、字符串结尾或a),则不要将其包含在此答案中。

推荐阅读