1

各行がゲーム ユニットを説明する、比較的単純なテキストを解析しています。解析手法についての知識がほとんどないため、次のアドホック ソリューションを使用しました。

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 混乱を避けるために、コードの一部をスキップしました。プロセスにエラーが発生した場合は、申し訳ありません-元のコードは機能します:)

4

1 に答える 1

4

私は pyparsing のコーチング ガイドを書き始めましたが、あなたのルールを見ると、EBNF を扱うことなく、pyparsing 要素自体にかなり簡単に変換されるので、簡単なサンプルを作成しました。

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 は演算子のオーバーロードを使用して、単純なパーサーから複雑なパーサーを構築します: '+' はシーケンス、'|' です。代替 (最初の一致または最長一致) の場合は '^' など。

解析された結果は次のようになります。正規表現で名前付きグループを使用したのと同じように、結果の名前を追加したことに注意してください。

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 からソース配布物をダウンロードすると、追加のドキュメントがたくさんあります。

于 2010-09-09T03:49:35.790 に答える