Захват блока по нескольким строкам с использованием pyparsing - PullRequest
1 голос
/ 29 апреля 2019

Попытка разобрать несколько выделенных элементов в многострочном документе. Хотите захватить все строки между каждым из ключевых слов. Вот пример:

Keyword 1: CAPTURE THIS TEXT
Keyword 2: CAPTURE THIS TEXT

Keyword 3:
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT

Keyword 4

У меня также может быть

Keyword 1: CAPTURE THIS TEXT
           CAPTURE THIS TEXT
Keyword 2: CAPTURE THIS TEXT

Keyword 3:
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT

Keyword 4

Мой код выглядит как

from pyparsing import *

EOL = LineEnd().suppress()
line = OneOrMore(Group(SkipTo(LineEnd()) + EOL))

KEYWORD_CAPTURE_AREA = Keyword("Keyword 1:").suppress() + line + Keyword("Keyword 2:").suppress() + line \
                    + Keyword("Keyword 3:").suppress() + line + Keyword("Keyword 4").suppress()

Текущий подход не возвращает результатов, если мой результат пересекает несколько строк. Предположим, что должно быть прямое решение этого - просто не нашли его.

Ответы [ 2 ]

1 голос
/ 30 апреля 2019

Концепция, которую необходимо изучить с помощью pyparsing, заключается в том, что каждое подвыражение выполняется само по себе, не зная ни о каких выражениях, содержащих или следующих. Поэтому, когда ваш line должен соответствовать одному или нескольким «пропустить до конца текущей строки», он не знает, что он должен остановиться, когда увидит следующую строку «Ключевое слово», и поэтому он предсказуемо читает до конца строки. Затем, когда анализатор переходит к поиску «Ключевое слово 2:», он уже далеко зашел, и поэтому возникает исключение.

Вам нужно сказать OneOrMore, что он должен прекратить синтаксический анализ, если обнаружит «Ключевое слово» в начале строки, даже если это обычно соответствует повторяющемуся выражению. Разумным определением конца блока может быть слово «Ключевое слово», если оно найдено в начале строки. (Вы можете сделать его более подробным и сопоставить "Keyword" + integer + ":", чтобы сделать его действительно пуленепробиваемым.) Давайте назовем это «start_of_block_marker»:

start_of_block_marker = LineStart() + "Keyword"

Чтобы сообщить OneOrMore, что это указывает на условие остановки для его повторения, передайте это выражение в качестве аргумента stopOn:

line = OneOrMore(Group(SkipTo(LineEnd()) + EOL), 
                 stopOn=LineStart() + "Keyword")

Теперь это проанализирует все ваши строки, но вы группируете внутри OneOrMore, когда я думаю, что вы действительно хотите, чтобы все подстроки были в одной группе. Кроме того, пустая строка между 2 и 3 создает дополнительную пустую строку. Вот улучшенная версия линии:

line = Optional(EOL) + Group(OneOrMore(SkipTo(LineEnd()) + EOL,
                             stopOn=LineStart() + "Keyword"))

Я поместил две тестовые строки в список, а затем использовал его в качестве аргумента для runTests():

text1 = """\
Keyword 1: CAPTURE THIS TEXT
           CAPTURE THIS TEXT
Keyword 2: CAPTURE THIS TEXT

Keyword 3:
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT

Keyword 4"""

text2 = """\
Keyword 1: CAPTURE THIS TEXT
Keyword 2: CAPTURE THIS TEXT

Keyword 3:
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT

Keyword 4
"""
KEYWORD_CAPTURE_AREA.runTests(tests)

Какие распечатки (повторение каждого теста, а затем печать проанализированных результатов):

Keyword 1: CAPTURE THIS TEXT
           CAPTURE THIS TEXT
Keyword 2: CAPTURE THIS TEXT

Keyword 3:
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT

Keyword 4
[['CAPTURE THIS TEXT', 'CAPTURE THIS TEXT'], ['CAPTURE THIS TEXT'], ['CAPTURE THIS TEXT', 'CAPTURE THIS TEXT', 'CAPTURE THIS TEXT', 'CAPTURE THIS TEXT']]
[0]:
  ['CAPTURE THIS TEXT', 'CAPTURE THIS TEXT']
[1]:
  ['CAPTURE THIS TEXT']
[2]:
  ['CAPTURE THIS TEXT', 'CAPTURE THIS TEXT', 'CAPTURE THIS TEXT', 'CAPTURE THIS TEXT']


Keyword 1: CAPTURE THIS TEXT
Keyword 2: CAPTURE THIS TEXT

Keyword 3:
CAPTURE THIS TEXT
CAPTURE THIS TEXT
CAPTURE THIS TEXT

Keyword 4

[['CAPTURE THIS TEXT'], ['CAPTURE THIS TEXT'], ['CAPTURE THIS TEXT', 'CAPTURE THIS TEXT', 'CAPTURE THIS TEXT']]
[0]:
  ['CAPTURE THIS TEXT']
[1]:
  ['CAPTURE THIS TEXT']
[2]:
  ['CAPTURE THIS TEXT', 'CAPTURE THIS TEXT', 'CAPTURE THIS TEXT']

Если в результатах есть ошибка, runTests() отобразит строку и местоположение проблемы и выдаст сообщение об ошибке pyparsing.

0 голосов
/ 29 апреля 2019

Это должно быть pyparsing?

Если нет, вы можете использовать split, например,

f = open('sample.txt')
values = []
for text in f.read().split('Keyword '):
    values.append(text[2:])
print(values)

>> ['', ' CAPTURE THIS TEXT\n           CAPTURE THIS TEXT\n', ' CAPTURE THIS TEXT\n\n', '\nCAPTURE THIS TEXT\nCAPTURE THIS TEXT\nCAPTURE THIS TEXT\nCAPTURE THIS TEXT\n\n', '']
...