Извлекайте вопросы и ответы с помощью регулярных выражений - PullRequest
2 голосов
/ 14 марта 2020

Я хочу извлечь некоторые вопросы и ответы из некоторых файлов, которые я читаю, но мое регулярное выражение не работает для меня:

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')]

Ответы [ 3 ]

2 голосов
/ 14 марта 2020

Вместо написания регулярного выражения монстра для многострочного требования, я бы использовал обычную итерацию и накопление, более или менее. Вы можете разделить на /\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')

Объяснения Regex:

  • /\n(?=[a-z\d][).] )/gs выполняет разбиение на новые строки, которые обращены к любому из двух a) или 1. паттернов. Это позволяет нам сохранять многострочные блоки.
  • /\d+\. (.+)/gs позволяет нам идентифицировать фрагмент 1. question и захватывать тело вопроса.
  • /[a-z]+\) (.+?)(?=\n\n|$|[a-z]+\) )/gs соответствует фрагменту a) answer. Это почти то же самое, что и фрагмент 1. question выше, но он должен быть немного осторожен, чтобы обрезать следующий заголовок контента, который не был обработан regex (1) выше. Это то, что делает (?=\n\n|$|[a-z]+\) ) lookahead: если следующее является двойной новой строкой, концом строки или a), то не включайте его в этот ответ.
1 голос
/ 14 марта 2020

Одним из более простых подходов будет разбиение каждой строки и применение regex, например:

import re


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

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

question = re.compile(r'^\d+\.\s(.+)')
answer = re.compile(r'^[a-z]\)\s(.+)')

output = []
for line in text.splitlines():
    if question.match(line):
        output.append(question.findall(line))
    elif answer.match(line):
        output[-1].append(answer.findall(line)[0])

print(output)
>>> [['question', 'answer', 'answer'], ['question', 'answer', 'answer'], ['question', 'answer', 'answer']]  
0 голосов
/ 14 марта 2020

Для многострочных вопросов и ответов вы можете попробовать это:

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

category 2
3. question 3
   second line of question 3
a) answer 3a
   second line of answer 3a
b) answer 3b
   second line of answer 3b
'''

quiz = []
for category in re.split("\n\n", text):
    qa = re.findall(r"^\d+\.\s+(.*?)(^[a-z][).](?:[^\n]|\n(?!\d))*)", category, re.DOTALL | re.MULTILINE)
    for question, answers in qa:       
        quiz.append((question.strip(), *re.findall(r"^[a-z][).]\s+((?:[^\n]|\n(?![a-z]))*)", answers, re.MULTILINE)))

print (quiz)

Выходные данные

[('question 1', 'answer 1a\n   second line of answer 1a', 'answer 1b'), ('question 2', 'answer 2a', 'answer 2b\n   second line of answer 2b'), ('question 3\n   second line of question 3', 'answer 3a\n   second line of answer 3a', 'answer 3b\n   second line of answer 3b\n')]

Поскольку в вопросе нет определения, как обрабатывать строки, заканчивающиеся / пробелы в Многострочные вопросы и ответы трудно понять, удовлетворяет ли этот вывод требованиям или нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...