Поиск ключевых слов в Pyparsing:нежадное поглощение токенов

StackOverflow https://stackoverflow.com/questions/1905278

Вопрос

Питонисты:

Предположим, вы хотите разобрать следующую строку с помощью Pyparsing:

'ABC_123_SPEED_X 123'

были ABC_123 является идентификатором; SPEED_X является параметром, и 123 это ценность.Я подумал о следующем BNF, используя Pyparsing:

Identifier = Word( alphanums + '_' )
Parameter = Keyword('SPEED_X') or Keyword('SPEED_Y') or Keyword('SPEED_Z')
Value = # assume I already have an expression valid for any value
Entry = Identifier + Literal('_') + Parameter + Value
tokens = Entry.parseString('ABC_123_SPEED_X 123')
#Error: pyparsing.ParseException: Expected "_" (at char 16), (line:1, col:17)

Если я уберу подчеркивание посередине (и отрегулирую Entry определение соответственно) он анализируется корректно.

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

Спасибо.

[Примечание:Это полная перепись моего вопроса;Я не понимал, в чем заключалась настоящая проблема]

Это было полезно?

Решение

Я основал свой ответ на этот, поскольку то, что вы пытаетесь сделать, - это получить нежадное совпадение.Кажется, что это трудно реализовать в pyparsing, но не невозможно при некоторой сообразительности и компромиссе.Кажется, работает следующее:

from pyparsing import *
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
UndParam = Suppress('_') + Parameter
Identifier = SkipTo(UndParam)
Value = Word(nums)
Entry = Identifier + UndParam + Value

Когда мы запускаем это из интерактивного интерпретатора, мы можем увидеть следующее:

>>> Entry.parseString('ABC_123_SPEED_X 123')
(['ABC_123', 'SPEED_X', '123'], {})

Обратите внимание, что это компромисс;потому что я использую SkipTo, тот самый Identifier может быть полно злых, отвратительных персонажей, а не просто красивых alphanums со случайным подчеркиванием.

Редактировать: Благодаря Полу Макгуайру мы можем придумать по-настоящему элегантное решение, установив Identifier к следующему:

Identifier = Combine(Word(alphanums) +
        ZeroOrMore('_' + ~Parameter + Word(alphanums)))

Давайте посмотрим, как это работает.Во-первых, игнорируйте внешнее Combine;мы вернемся к этому позже.Начиная с Word(alphanums) мы знаем, что получим 'ABC' часть ссылочной строки, 'ABC_123_SPEED_X 123'.Важно отметить, что в данном случае мы не разрешили "слову" содержать подчеркивания.Мы встраиваем это отдельно в логику.

Далее, нам нужно зафиксировать '_123' расстаться, не всасывая при этом '_SPEED_X'.Давайте также перейдем к следующему ZeroOrMore остановитесь на этом моменте и вернитесь к нему позже.Мы начинаем с подчеркивания в виде Literal, но мы можем сократить путь с помощью просто '_', что даст нам начальное подчеркивание, но не все '_123'.Инстинктивно мы бы разместили еще один Word(alphanums) чтобы захватить остальное, но это именно то, что доставит нам неприятности, если мы потребим все оставшееся '_123_SPEED_X'.Вместо этого мы говорим: "При условии, что то, что следует за подчеркиванием, является нет тот самый Parameter, проанализируйте это как часть моего Identifier.Мы утверждаем, что в терминах pyparsing как '_' + ~Parameter + Word(alphanums).Поскольку мы предполагаем, что у нас может быть произвольное количество повторений подчеркивания + WordButNotParameter, мы оборачиваем это выражение a ZeroOrMore сконструировать.(Если вы всегда ожидаете, что после инициала следует хотя бы символ подчеркивания + WordButNotParameter, вы можете использовать OneOrMore.)

Наконец, нам нужно обернуть начальное слово и специальное подчеркивание + Слово, повторяющееся вместе, чтобы было понятно, что они смежные, не разделенные пробелом, поэтому мы оборачиваем все выражение в Combine сконструировать.Сюда 'ABC _123_SPEED_X' вызовет ошибку синтаксического анализа, но 'ABC_123_SPEED_X' будет правильно проанализирован.

Обратите также внимание, что мне пришлось изменить Keyword Для Literal потому что способы первых слишком утонченны и быстры, чтобы вызвать гнев.Я не доверяю Keywords, и я также не мог найти с ними общий язык.

Другие советы

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

from pyparsing import *

my_string = 'ABC_123_SPEED_X 123'

Identifier = Combine(Word(alphanums) + Literal('_') + Word(alphanums))
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
Value = Word(nums)
Entry = Identifier + Literal('_').suppress() + Parameter  + Value
tokens = Entry.parseString(my_string)

print tokens # prints: ['ABC_123', 'SPEED_X', '123']

Если это не так, но если длина идентификатора фиксирована, вы можете определить идентификатор следующим образом:

Identifier = Word( alphanums + '_' , exact=7)

Вы также можете проанализировать идентификатор и параметр как один токен и разделить их в действии синтаксического анализа:

from pyparsing import *
import re

def split_ident_and_param(tokens):
    mo = re.match(r"^(.*?_.*?)_(.*?_.*?)$", tokens[0])
    return [mo.group(1), mo.group(2)]

ident_and_param = Word(alphanums + "_").setParseAction(split_ident_and_param)
value = Word(nums)
entry = ident_and_param + value

print entry.parseString("APC_123_SPEED_X 123")

В приведенном выше примере предполагается, что идентификаторы и параметры всегда имеют формат XXX_YYY (содержащий один-единственный символ подчеркивания).

Если это не так, вам необходимо скорректировать split_ident_and_param() способ.

Это ответ на вопрос, который вы, вероятно, тоже задавали себе:"Для чего нужно реальное приложение reduce?):

>>> keys = ['CAT', 'DOG', 'HORSE', 'DEER', 'RHINOCEROS']
>>> p = reduce(lambda x, y: x | y, [Keyword(x) for x in keys])
>>> p
{{{{"CAT" | "DOG"} | "HORSE"} | "DEER"} | "RHINOCEROS"}

Редактировать:

Это был довольно хороший ответ на первоначальный вопрос.Мне придется поработать над новым.

Дальнейшее редактирование:

Я почти уверен, что ты не сможешь сделать то, что пытаешься сделать.Синтаксический анализатор, который pyparsing creates не заглядывает вперед.Так что, если вы скажете, что это соответствует Word(alphanums + '_'), он будет продолжать сопоставлять символы до тех пор, пока не найдет тот, который не является буквой, цифрой или подчеркиванием.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top