Изящный парсинг файла структурированного текста

Pypi имеет эту запись для pygn, которая на самом деле относится к проекту под названием pyg . Похоже, вам нужно вручную включить этот репо как часть того, что вы хотите сделать.

20
задан russtbarnacle 21 October 2008 в 23:00
поделиться

9 ответов

Рассмотрите использование Ragel http://www.complang.org/ragel/

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

2
ответ дан 29 November 2019 в 23:23
поделиться

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

0
ответ дан 29 November 2019 в 23:23
поделиться

Я использовал pyParsing библиотеку классов Paul McGuire, и я продолжаю впечатляться ею, в котором она хорошо зарегистрирована, легка начать, и правила легко настроить и поддержать. BTW, правила выражаются в Вашем коде Python. Конечно, кажется, что файл журнала имеет достаточно регулярности для парсинга каждой строки как автономной единицы.

2
ответ дан 29 November 2019 в 23:23
поделиться

С Perl можно использовать Синтаксический анализ:: RecDescent

Это просто, и Ваша грамматика, будет удобен в сопровождении позже.

11
ответ дан 29 November 2019 в 23:23
поделиться

Вы могли бы хотеть рассмотреть полный парсер-генератор.

Регулярные выражения хороши для поиска текста для маленьких подстрок, но они являются горестно недостаточно мощными, если Вы действительно интересуетесь парсингом всего файла в значимые данные.

Они особенно недостаточны, если контекст подстроки важен.

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

Regexes являются большими и все, но если Вам нужен синтаксический анализатор, они не замена.

6
ответ дан 29 November 2019 в 23:23
поделиться

Используя мультилинию, прокомментировал, что regexs может смягчить maintainance проблему несколько. Старайтесь избегать одной строки супер regex!

кроме того, считайте разламывание regex на отдельными задачами, один для каждой 'вещи', которую Вы хотите получить. например,

visitor = text.find(/Visitor:(.*)/)
operator = text.find(/Operator:(.*)/)
body = text.find(/whatever....)

вместо

text.match(/Visitor:(.*)\nOperator:(.*)...whatever to giant regex/m) do
  visitor = $1
  operator = $2
  etc.
end

Затем это помогает измениться, как какой-то конкретный объект анализируется. До парсинга через файл со многими "блоками чата", просто имейте единственный простой regex, который соответствует единственному блоку чата, выполните итерации по тексту и передайте данные соответствия от этого до Вашей группы других matchers.

Это будет, очевидно, влиять на производительность, но если Вы обрабатывающий огромный файлы я не волновался бы.

4
ответ дан 29 November 2019 в 23:23
поделиться

Сборка синтаксический анализатор ? Я не могу решить, являются ли Ваши данные достаточно регулярными для этого, но это могло бы стоить изучить.

5
ответ дан 29 November 2019 в 23:23
поделиться

Нет и на самом деле, для определенного типа задачи Вы описываете, я сомневаюсь, что существует "более чистый" способ сделать это, чем регулярные выражения. Похоже, что Ваши файлы встраивали разрывы строки так обычно, что мы сделаем, вот, делают строку Вашей единицей разложения, применяясь на строку regexes. Между тем Вы создаете маленький конечный автомат и используете соответствия regex для инициирования переходов в том конечном автомате. Таким образом, Вы знаете, где Вы находитесь в файле, и какие типы символьных данных можно ожидать. Кроме того, рассмотрите использование названных групп получения и загрузки regexes из внешнего файла. Тот путь, если формат Ваших изменений расшифровки стенограммы, это - простой вопрос тонкой настройки regex, вместо того, чтобы писать новый определенный для синтаксического анализа код.

12
ответ дан 29 November 2019 в 23:23
поделиться

Вот два парсера, основанные на библиотеке генератора парсеров lepl . Они оба дают одинаковый результат.

from pprint import pprint
from lepl import AnyBut, Drop, Eos, Newline, Separator, SkipTo, Space

# field = name , ":" , value
name, value = AnyBut(':\n')[1:,...], AnyBut('\n')[::'n',...]    
with Separator(~Space()[:]):
    field = name & Drop(':') & value & ~(Newline() | Eos()) > tuple

header_start   = SkipTo('Chat Transcript' & Newline()[2])
header         = ~header_start & field[1:] > dict
server_message = Drop('* ') & AnyBut('\n')[:,...] & ~Newline() > 'Server'
conversation   = (server_message | field)[1:] > list
footer_start   = 'Visitor Details' & Newline() & '-'*15 & Newline()
footer         = ~footer_start & field[1:] > dict
chat_log       = header & ~Newline() & conversation & ~Newline() & footer

pprint(chat_log.parse_file(open('chat.log')))

Более строгий синтаксический анализатор

from pprint import pprint
from lepl import And, Drop, Newline, Or, Regexp, SkipTo

def Field(name, value=Regexp(r'\s*(.*?)\s*?\n')):
    """'name , ":" , value' matcher"""
    return name & Drop(':') & value > tuple

Fields = lambda names: reduce(And, map(Field, names))

header_start   = SkipTo(Regexp(r'^Chat Transcript$') & Newline()[2])
header_fields  = Fields("Visitor Operator Company Started Finished".split())
server_message = Regexp(r'^\* (.*?)\n') > 'Server'
footer_fields  = Fields(("Your Name, Your Question, IP Address, "
                         "Host Name, Referrer, Browser/OS").split(', '))

with open('chat.log') as f:
    # parse header to find Visitor and Operator's names
    headers, = (~header_start & header_fields > dict).parse_file(f)
    # only Visitor, Operator and Server may take part in the conversation
    message = reduce(Or, [Field(headers[name])
                          for name in "Visitor Operator".split()])
    conversation = (message | server_message)[1:]
    messages, footers = ((conversation > list)
                         & Drop('\nVisitor Details\n---------------\n')
                         & (footer_fields > dict)).parse_file(f)

pprint((headers, messages, footers))

Вывод:

({'Company': 'Initech',
  'Finished': '16 Oct 2008 9:45:44',
  'Operator': 'Milton',
  'Started': '16 Oct 2008 9:13:58',
  'Visitor': 'Random Website Visitor'},
 [('Random Website Visitor',
   'Where do i get the cover sheet for the TPS report?'),
  ('Server',
   'There are no operators available at the moment. If you would like to leave a message, please type it in the input field below and click "Send" button'),
  ('Server',
   'Call accepted by operator Milton. Currently in room: Milton, Random Website Visitor.'),
  ('Milton', 'Y-- Excuse me. You-- I believe you have my stapler?'),
  ('Random Website Visitor', 'I really just need the cover sheet, okay?'),
  ('Milton',
   "it's not okay because if they take my stapler then I'll, I'll, I'll set the building on fire..."),
  ('Random Website Visitor', 'oh i found it, thanks anyway.'),
  ('Server',
   'Random Website Visitor is now off-line and may not reply. Currently in room: Milton.'),
  ('Milton', "Well, Ok. But… that's the last straw."),
  ('Server',
   'Milton has left the conversation. Currently in room:  room is empty.')],
 {'Browser/OS': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727)',
  'Host Name': '255.255.255.255',
  'IP Address': '255.255.255.255',
  'Referrer': 'Unknown',
  'Your Name': 'Random Website Visitor',
  'Your Question': 'Where do i get the cover sheet for the TPS report?'})
6
ответ дан 29 November 2019 в 23:23
поделиться