Парсинг RTF с помощью pyparsing

Я пытаюсь реализовать простой общий анализатор RTF с помощью pyparsing. Но теперь я застрял в странной ошибке:

    #Code:
    control_codes = Word('\\;*' + alphanums)
    start = Word('{')
    end = Word('}') | (Word(';') + Word('}'))
    header = OneOrMore(control_codes | Word(alphanums))

    document = Forward()
    document <<= (
        start +
        header +
        (document + end) | end
    )
    print document.parseString('{\\*\\falt Times New Roman}')

    #Error
    pyparsing.ParseException: Expected W:(}) (at char 25), (line:1, col:26)

Символ 25 — это '}', это последний символ в строке. Это самый простой пример, более сложные примеры, которые я пытался использовать, обрезали часть входной строки.


person beatt    schedule 16.11.2013    source источник


Ответы (1)


На самом деле у вас есть ряд ошибок в этом фрагменте, и вы можете пересмотреть некоторые документы по pyparsing, чтобы убедиться, что у вас есть основы.

Word(stringOfChars) будет соответствовать группе символов, которые все существуют в данном stringOfChars. Word(alphanums) будет соответствовать «ABC», «slkjfljlsdflsdjf», «2330098324», «392084lsfd0238» и так далее. Таким образом, Word("{") будет соответствовать не только "{", но также "{{" и "{{{{{{{{{"}} — в некоторых из этих мест, где вы определяете знаки препинания, я думаю, вы, скорее всего, имеете в виду использование Literal, не Word. Literal будет соответствовать только точной строке, используемой в его конструкторе, без неявного повторения.

Word(string1, string2) будет соответствовать группе символов, начинающихся с одного из символов в string1, за которым сразу следует ноль или более символов в string2. В вашем определении controlCodes, я думаю, вы хотите принять только начальный символ «\», за которым следует 1 или более alphanums, «*» или «;». Правильнее было бы написать как Word('\\', alphanums+'*'+';')

Поскольку вы анализируете RTF, который часто включает символы '\', вы добьетесь большего прогресса, если будете использовать необработанные строковые литералы Python, что позволит вам вводить строки, содержащие обратную косую черту, без необходимости удваивать их. Сравнивать

print document.parseString('{\\*\\falt Times New Roman}')

и

print document.parseString(r'{\*\falt Times New Roman}')

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

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

header.setName("header")
header.setDebug()

Это будет распечатывать диагностические сообщения каждый раз, когда в синтаксическом анализаторе будет достигнуто header, и показывать, был ли синтаксический анализ успешным или нет. Если синтаксический анализ прошел успешно, он также покажет, что было сопоставлено. Вот что вы получите при разборе вашего примера текста:

Match header at loc 1(1,2)
Matched header -> ['\\', '*', '\\falt', 'Times', 'New', 'Roman']
Traceback (most recent call last):
  File "rtf.py", line 26, in <module>
    print document.parseString(r'{\*\falt Times New Roman}')
  File "c:\python26\lib\site-packages\pyparsing.py", line 1041, in parseString
    raise exc
pyparsing.ParseException: Expected "}" (at char 1), (line:1, col:2)

Я не думаю, что вы ожидали, что header прочитает "Times New Roman" до конца.

Если вы еще не успели начертить БНФ для своего синтаксического анализатора, я настоятельно рекомендую вам сделать это. После того, как вы это запишете, попробуйте проследить БНФ через вашу тестовую строку - будьте настолько буквальны, насколько сможете, не делайте НИКАКИХ предположений о том, где OneOrMore выражения должны останавливаться, потому что "это очевидно". Pyparsing не выполняет никаких действий, о которых вы не говорите.

person PaulMcG    schedule 16.11.2013
comment
Спасибо за объяснение. Я решил проблему в этой теме. pyparsing - отличная библиотека, как я вижу. - person beatt; 16.11.2013