копирование захвата групп произвольного текста с заданными заголовками в виде вложенных списков - PullRequest
8 голосов
/ 20 февраля 2012

У меня есть текстовый файл, который выглядит примерно так:

заголовок раздела 1:
некоторые слова могут быть чем угодно
больше слов может быть чем угодно
и т. Д. И т. Д. Lala

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

Я пытаюсь создать грамматику с помощью pyparser, которая приведет к следующему спискуструктура при запросе проанализированных результатов в виде списка;(IE; следующее должно быть напечатано при переборе элементов parsed.asList ())

['заголовок раздела 1:', [['некоторые слова могут быть чем угодно'], ['moreслова могут быть чем угодно '], [' etc etc lala ']]]
[' какой-то другой заголовок: ', [[' как и раньше может быть что угодно '], [' эй, разве это не весело ']]]

Все имена заголовков известны заранее, и отдельные заголовки могут появляться или не появляться.Если они появляются, то всегда есть по крайней мере одна строка контента.

Проблема, с которой я столкнулся, заключается в том, что у меня возникают проблемы с получением парсера для распознавания, где «заголовок раздела 1:» и «и» некоторыедругой заголовок: «начинается.Я получаю parsed.asList (), который выглядит так:

['заголовок раздела 1:', [['' некоторые слова могут быть чем угодно '], [' больше слов может быть чем угодно вall '], [' etc etc lala '], [' некоторый другой заголовок '], [' 'как и раньше могло быть что угодно'], ['hey isnt this fun']]]

(IE: заголовок раздела 1: отображается правильно, но все, что следует за ним, добавляется в заголовок раздела 1, включая дополнительные строки заголовка и т. Д.)

Я пробовал разные вещи, поиграл с помощью leftWhitespace () и LineEnd ()различными способами, но я не могу понять это.

Базовый парсер, с которым я взламываю - это (надуманный пример - на самом деле это определение класса и т. д.).

header_1_line=Literal('section header 1:')

text_line=Group(OneOrMore(Word(printables)))

header_1_block=Group(header_1_line+Group(OneOrMore(text_line)))

header_2_line=Literal('some other header:')

header_2_block=Group(header_2_line+Group(OneOrMore(text_line)))

overall_structure=ZeroOrMore(header_1_block|header_2_block)

и вызывается с

parsed=overall_structure.parseFile()

Cheers, Matt.

1 Ответ

12 голосов
/ 20 февраля 2012

Мэтт -

Добро пожаловать на pyparsing!Вы попали в одну из самых распространенных ловушек в работе с pyparsing, и это то, что люди умнее компьютеров.Когда вы смотрите на введенный текст, вы можете легко увидеть, какой текст может быть заголовком, а какой - нет.К сожалению, pyparsing не так интуитивно понятен, поэтому вы должны сказать , что именно может и не может быть текстом.

Когда вы смотрите на образец текста, вы не принятие только любой строки текста в качестве возможного текста в заголовке раздела.Откуда вы знаете, что «какой-то другой заголовок:» недействителен как текст?Потому что вы знаете, что эта строка соответствует одной из известных строк заголовка.Но в своем текущем коде вы сказали pyparsing, что любая коллекция Word(printables) является допустимым текстом, , даже если эта коллекция является действительным заголовком раздела .

Чтобы исправить это, вы должныдобавьте некоторый явный взгляд на ваш парсер.Pyparsing предлагает две конструкции, NotAny и FollowedBy.NotAny может быть сокращено с помощью оператора '~', поэтому мы можем записать это выражение псевдокода для текста:

text = ~any_section_header + everything_up_to_the_end_of_the_line

Вот полный анализатор, использующий отрицательный взгляд, чтобы убедиться, что вы читаете каждый раздел, разбивая заголовки разделов:

from pyparsing import ParserElement, LineEnd, Literal, restOfLine, ZeroOrMore, Group, StringEnd

test = """
section header 1:
 some words can be anything
 more words could be anything at all
 etc etc lala 

some other header:
 as before could be anything
 hey isnt this fun
"""
ParserElement.defaultWhitespaceChars=(" \t")
NL = LineEnd().suppress()
END = StringEnd()

header_1=Literal('section header 1:') 
header_2=Literal('some other header:')
any_header = (header_1 | header_2)
# text isn't just anything! don't accept header line, and stop at the end of the input string
text=Group(~any_header + ~END + restOfLine) 

overall_structure = ZeroOrMore(Group(any_header +
                                     Group(ZeroOrMore(text))))
overall_structure.ignore(NL)

from pprint import pprint
print(overall_structure.parseString(test).asList())

В моей первой попытке я забыл также искать конец строки, поэтому мое выражение restOfLine зациклилось навсегда.Добавив второй запрос конца строки, моя программа успешно завершается.Упражнение, оставленное для вас: вместо перечисления всех возможных заголовков определите строку заголовка как любую строку, заканчивающуюся символом ':'.

Удачи в ваших усилиях по переносу, - Пол

...