3 Stimmen

Was ist der beste Weg (fehlerfrei / narrensicher), um eine Datei mit Python mit folgendem Format zu parsen?

########################################
# some comment
# other comment
########################################

block1 {
    value=data
    some_value=some other kind of data
    othervalue=032423432
    }

block2 {
    value=data
    some_value=some other kind of data
    othervalue=032423432
    }

6voto

jfs Punkte 370717

Am besten wäre es, ein bestehendes Format wie JSON zu verwenden.

Hier ist ein Beispielparser für Ihr Format:

from lepl import (AnyBut, Digit, Drop, Eos, Integer, Letter,
                  NON_GREEDY, Regexp, Space, Separator, Word)

# EBNF
# name = ( letter | "_" ) , { letter | "_" | digit } ;
name = Word(Letter() | '_',
            Letter() | '_' | Digit())
# words = word , space+ , word , { space+ , word } ;
# two or more space-separated words (non-greedy to allow comment at the end)
words = Word()[2::NON_GREEDY, ~Space()[1:]] > list
# value = integer | word | words  ;
value = (Integer() >> int) | Word() | words
# comment = "#" , { all characters - "\n" } , ( "\n" | EOF ) ;
comment = '#' & AnyBut('\n')[:] & ('\n' | Eos())

with Separator(~Regexp(r'\s*')):
    # statement = name , "=" , value ;
    statement = name & Drop('=') & value > tuple
    # suite     = "{" , { comment | statement } , "}" ;
    suite     = Drop('{') & (~comment | statement)[:] & Drop('}') > dict
    # block     = name , suite ;
    block     = name & suite > tuple
    # config    = { comment | block } ;
    config    = (~comment | block)[:] & Eos() > dict

from pprint import pprint

pprint(config.parse(open('input.cfg').read()))

Ausgabe:

[{'block1': {'othervalue': 32423432,
             'some_value': ['some', 'other', 'kind', 'of', 'data'],
             'value': 'data'},
  'block2': {'othervalue': 32423432,
             'some_value': ['some', 'other', 'kind', 'of', 'data'],
             'value': 'data'}}]

1 Stimmen

@vak: lepl wird aufgegeben . Ich habe veröffentlicht Grako-basierte Lösung .

4voto

John Fouhy Punkte 39035

Nun, die Daten sehen ziemlich regelmäßig aus. Sie könnten also etwas wie folgt tun (nicht getestet):

class Block(object):
    def __init__(self, name):
        self.name = name

infile = open(...)  # insert filename here
current = None
blocks = []

for line in infile:
    if line.lstrip().startswith('#'):
        continue
    elif line.rstrip().endswith('{'):
        current = Block(line.split()[0])
    elif '=' in line:
        attr, value = line.strip().split('=')
        try:
            value = int(value)
        except ValueError:
            pass
        setattr(current, attr, value)
    elif line.rstrip().endswith('}'):
        blocks.append(current)

Das Ergebnis ist eine Liste von Block-Instanzen, wobei block.name ist der Name ( 'block1' , 'block2' usw.) und andere Attribute entsprechen den Schlüsseln in Ihren Daten. Also, blocks[0].value wird zu "Daten" usw. Beachten Sie, dass dies nur Strings und Ganzzahlen als Werte behandelt.

(Es gibt hier einen offensichtlichen Fehler, wenn Ihre Schlüssel jemals 'Name' enthalten können. Sie könnten Folgendes ändern self.name a self._name oder etwas anderes, wenn dies geschehen kann)

HTH!

0 Stimmen

Wenn die Eingabedatei inválido und beginnt mit value=data wird Ihr Programm abstürzen, weil current nicht initialisiert worden ist. Wie das alte Sprichwort sagt, "Müll rein, Müll raus" . Auf jeden Fall +1 von mir.

3voto

Bartosz Radaczyński Punkte 18134

Wenn Sie nicht wirklich Parsing, sondern eher Textverarbeitung meinen und die Eingabedaten wirklich so regelmäßig sind, dann nehmen Sie Johns Lösung. Wenn Sie wirklich Parsing benötigen (z. B. wenn die Daten, die Sie erhalten, etwas komplexer sind), dann würde ich, je nach der Menge der zu parsenden Daten, entweder pyparsing oder simpleparse . Ich habe beide ausprobiert, aber eigentlich war mir pyparsing zu langsam.

2voto

Fred Larson Punkte 58721

Sie könnten sich mit folgenden Themen befassen pyparsing.

1voto

jfs Punkte 370717

Grako (für Grammatik-Compiler) ermöglicht es, die Spezifikation des Eingabeformats (Grammatik) von seiner Interpretation (Semantik) zu trennen. Hier ist die Grammatik für Ihr Eingabeformat in Grakos Variante von EBNF :

(* a file contains zero or more blocks *)
file = {block} $;
(* a named block has at least one assignment statement *)
block = name '{' {assignment}+ '}';
assignment = name '=' value NEWLINE;
name = /[a-z][a-z0-9_]*/;
value = integer | string;
NEWLINE = /\n/;
integer = /[0-9]+/;
(* string value is everything until the next newline *)
string = /[^\n]+/;

Zum Installieren grako , laufen pip install grako . Zur Erzeugung der PEG Parser aus der Grammatik:

$ grako -o config_parser.py Config.ebnf

Um stdin in json zu konvertieren, verwenden Sie die generierte config_parser Modul:

#!/usr/bin/env python
import json
import string
import sys
from config_parser import ConfigParser

class Semantics(object):
    def file(self, ast):
        # file = {block} $
        # all blocks should have unique names within the file
        return dict(ast)
    def block(self, ast):
        # block = name '{' {assignment}+ '}'
        # all assignment statements should use unique names
        return ast[0], dict(ast[2])
    def assignment(self, ast):
        # assignment = name '=' value NEWLINE
        # value = integer | string
        return ast[0], ast[2] # name, value
    def integer(self, ast):
        return int(ast)
    def string(self, ast):
        return ast.strip() # remove leading/trailing whitespace

parser = ConfigParser(whitespace='\t\n\v\f\r ', eol_comments_re="#.*?$")
ast = parser.parse(sys.stdin.read(), rule_name='file', semantics=Semantics())
json.dump(ast, sys.stdout, indent=2, sort_keys=True)

Ausgabe

{
  "block1": {
    "othervalue": 32423432,
    "some_value": "some other kind of data",
    "value": "data"
  },
  "block2": {
    "othervalue": 32423432,
    "some_value": "some other kind of data",
    "value": "data"
  }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X