簡體   English   中英

python:用BNF或pyparsing替換正則表達式

[英]python: replacing regex with BNF or pyparsing

我正在解析一個相對簡單的文本,其中每一行描述一個游戲單元。 我對解析技術知之甚少,所以我使用了以下特殊解決方案:

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 ''

它工作正常,但我覺得我達到了正則表達式的極限。 具體來說,在Traits的情況下,該值最終成為我需要拆分並在以后轉換為列表的字符串:例如,obj.Traits將在此代碼中設置為“Motorized Armored”,但在后來的功能改為('Motorized','Armored')。

我正在考慮將此代碼轉換為使用EBNF或pyparsing語法或類似的東西。 我的目標是:

  • 使這個代碼更整潔,更不容易出錯
  • 避免使用值列表對案例進行丑陋處理(我需要先在正則表達式中進行替換,然后對結果進行后處理以將字符串轉換為列表)

您對使用什么以及如何重寫代碼有什么建議?

PS我跳過代碼的某些部分以避免混亂; 如果我在這個過程中引入了任何錯誤,抱歉 - 原始代碼確實有效:)

我開始寫一篇關於pyparsing的教練指南,但是看看你的規則,他們很容易將它們轉換成pyparsing元素本身,而不用處理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示例,因此您可以看到如何將正則表達式放入現有的pyparsing語法中。 使用'&'運算符的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來安裝pyparsing,但是如果從SourceForge下載源代碼發布,則還有許多其他文檔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM