Domanda

Io sono l'analisi di un testo relativamente semplice, in cui ogni riga descrive un'unità di gioco. Ho poca conoscenza delle tecniche di parsing, quindi ho usato la seguente soluzione ad hoc:

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

Funziona bene, ma mi sento ho raggiunto il limite del potere regex. In particolare, nel caso di tratti, il valore finisce per essere una stringa che devono dividere e convertire in una lista in un momento successivo: ad esempio, obj.Traits verrebbero impostati su 'motorizzato corazzata' in questo codice, ma in un funzione poi cambiato in ( 'motorizzato', 'blindata').

Sto pensando di convertire il codice per utilizzare EBNF o pyparsing grammatica o qualcosa del genere. I miei obiettivi sono:

  • rendere il codice più ordinato e meno soggetto a errori
  • evitare il brutto trattamento del caso, con un elenco di valori (dove ho bisogno di fare la sostituzione all'interno della regex prima, e poi post-processo il risultato per convertire una stringa in una lista)

Quali sarebbero i vostri suggerimenti su cosa utilizzare e come riscrivere il codice?

P.S. Ho saltato alcune parti del codice per evitare il disordine; se ho introdotto eventuali errori nel processo, mi dispiace - il codice originale funziona:)

È stato utile?

Soluzione

Ho iniziato a scrivere una guida coaching per pyparsing, ma guardando le regole, si traducono abbastanza facilmente in pyparsing elementi stessi, senza trattare con EBNF, così ho cucinato un campione rapida:

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

I incluso l'esempio Regex così si potrebbe vedere come un'espressione regolare potrebbe essere eliminato per una grammatica pyparsing esistente. La composizione del rule usando 'e degli operatori significa che le singole voci potrebbe essere trovato in qualsiasi ordine (così la grammatica si prende cura del iterare su tutte le regole, invece di farlo nel proprio codice). Pyparsing usi l'overloading degli operatori per costruire parser complessi da quelli semplici: '+' per la sequenza, '|' e '^' alternative (primo stadio e lungo-match), e così via.

Ecco come i risultati analizzati apparirebbe - nota che ho aggiunto risultati nomi, proprio come si è utilizzato gruppi citati nel vostro 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)

stampe:

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

Si prega di fermare dal wiki e vedere gli altri esempi. È possibile installare easy_install pyparsing, ma se si scarica la distribuzione del codice sorgente da SourceForge, c'è un sacco di documentazione aggiuntiva.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top