Вопрос

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

class Unit:
    # rules is an ordered dictionary of tagged regex that is intended to be applied in the given order
    # the group named V would correspond to the value (if any) for that particular tag
    rules = (
        ('Level', r'Lv. (?P<V>\d+)'),
        ('DPS', r'DPS: (?P<V>\d+)'),
        ('Type', r'(?P<V>Tank|Infantry|Artillery'),
        #the XXX will be expanded into a list of valid traits
        #note: (XXX| )* wouldn't work; it will match the first space it finds,
        #and stop at that if it's in front of something other than a trait
        ('Traits', r'(?P<V>(XXX)(XXX| )*)'),
        # flavor text, if any, ends with a dot
        ('FlavorText', r'(?P<V>.*\."?$)'),
        )
    rules = collections.OrderedDict(rules)
    traits = '|'.join('All-Terrain', 'Armored', 'Anti-Aircraft', 'Motorized')
    rules['Traits'] = re.sub('XXX', effects, rules['Traits'])

    for x in rules:
        rules[x] = re.sub('<V>', '<'+x+'>', rules[x])
        rules[x] = re.compile(rules[x])

    def __init__(self, data)
        # data looks like this:
        # Lv. 5 Tank DPS: 55 Motorized Armored
        for field, regex in Item.rules.items():
            data = regex.sub(self.parse, data, 1)
        if data:
            raise ParserError('Could not parse part of the input: ' + data)

    def parse(self, m):
        if len(m.groupdict()) != 1:
            Exception('Expected a single named group')
        field, value = m.groupdict().popitem()
        setattr(self, field, value)
        return ''

Работает нормально, но я чувствую, что добился пределов питания Regex. В частности, в случае черт значения заканчивается строкой, которая мне нужна для разделения и преобразования в список в более позднем этапе: например, obj.traits будет установлен на «моторизованный бронированный» в этом коде, но в Позже функция изменилась на («моторизованный», «бронетанко»).

Я думаю преобразовывать этот код, чтобы использовать либо EBNF, либо Pyparding грамматику или что-то в этом роде. Мои цели:

  • Сделайте этот код Neater и менее подверженным ошибкам
  • Избегайте уродливого лечения корпуса со списком значений (где мне нужно сделать замену внутри Regex сначала, а затем пост-процесс результат для преобразования строки в список)

Каковы ваши предложения о том, что использовать, и как переписать код?

PS Я пропустил некоторые части кода, чтобы избежать беспорядка; Если я представил любые ошибки в процессе, извините - оригинальный код работает :)

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

Решение

Я начал писать руководство для коучинга для Pyparting, но глядя на ваши правила, они довольно легко переводят в сами элементы Pyparting, не имеют дело с EBNF, поэтому я просто приготовил быстрый образец:

from pyparsing import Word, nums, oneOf, Group, OneOrMore, Regex, Optional

integer = Word(nums)
level = "Lv." + integer("Level")
dps = "DPS:" + integer("DPS")
type_ = oneOf("Tank Infantry Artillery")("Type")
traits = Group(OneOrMore(oneOf("All-Terrain Armored Anti-Aircraft Motorized")))("Traits")
flavortext = Regex(r".*\.$")("FlavorText")

rule = (Optional(level) & Optional(dps) & Optional(type_) & 
        Optional(traits) & Optional(flavortext))

Я включил пример Regex, чтобы вы могли видеть, как регулярное выражение может быть отброшено в существующую гиммаматику Pyparding. Состав rule Использование операторов «&» означает, что отдельные предметы могут быть найдены в любом порядке (поэтому грамматика позаботится о итерации по всем правилам, а не в свой собственный код). Pyparsing использует перегрузку оператора для создания комплексных парсеров от простых: «+» для последовательности «|» и «^» для альтернатив (первый матч или самый длинный матч), и так далее.

Вот как будут выглядеть анализированные результаты - обратите внимание, что я добавил имена результатов, как вы использовали именованные группы в вашем Regexen:

data = "Lv. 5 Tank DPS: 55 Motorized Armored"

parsed_data = rule.parseString(data)
print parsed_data.dump()
print parsed_data.DPS
print parsed_data.Type
print ' '.join(parsed_data.Traits)

Печать:

['Lv.', '5', 'Tank', 'DPS:', '55', ['Motorized', 'Armored']]
- DPS: 55
- Level: 5
- Traits: ['Motorized', 'Armored']
- Type: Tank
55
Tank
Motorized Armored

Пожалуйста, остановитесь на вики и посмотрите другие примеры. Вы можете Easy_install, чтобы установить Pyparding, но если вы загрузите дистрибуцию источника из SourceForge, есть много дополнительной документации.

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