Каков наилучший способ (защищенный от ошибок / надежный) для анализа файла с использованием Python следующего формата? - PullRequest
3 голосов
/ 30 января 2009
########################################
# 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
    }

Ответы [ 5 ]

6 голосов
/ 31 октября 2009

Лучший способ - использовать существующий формат, такой как JSON.

Вот пример парсера для вашего формата:

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

Выход:

[{'block1': {'othervalue': 32423432,
             'some_value': ['some', 'other', 'kind', 'of', 'data'],
             'value': 'data'},
  'block2': {'othervalue': 32423432,
             'some_value': ['some', 'other', 'kind', 'of', 'data'],
             'value': 'data'}}]
4 голосов
/ 30 января 2009

Ну, данные выглядят довольно регулярно. Таким образом, вы можете сделать что-то вроде этого (не проверено):

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)

Результатом будет список экземпляров блоков, где block.name будет именем ('block1', 'block2' и т. Д.), А другие атрибуты соответствуют ключам в ваших данных. Итак, blocks[0].value будет 'данными' и т. Д. Обратите внимание, что в качестве значений обрабатываются только строки и целые числа.

(здесь есть очевидная ошибка, если ваши ключи когда-либо могут содержать «имя». Вы можете изменить self.name на self._name или что-то еще, если это может произойти)

НТН!

3 голосов
/ 30 января 2009

Если вы на самом деле имеете в виду не разбор, а обработку текста, а входные данные действительно такие регулярные, то воспользуйтесь решением Джона. Если вам действительно нужен какой-то синтаксический анализ (например, есть некоторые более сложные правила для данных, которые вы получаете), то в зависимости от объема данных, которые вам нужно проанализировать, я бы выбрал либо pyparsing, либо simpleparse . Я попробовал оба из них, но на самом деле pyparsing был слишком медленным для меня.

2 голосов
/ 30 января 2009

Вы можете посмотреть что-то вроде pyparsing.

1 голос
/ 05 октября 2014

Grako (для компилятора грамматики) позволяет отделить спецификацию входного формата (грамматику) от его интерпретации (семантики). Вот грамматика для вашего входного формата в разнообразии Грако 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]+/;

Чтобы установить grako, запустите pip install grako. Чтобы сгенерировать синтаксический анализатор PEG из грамматики:

$ grako -o config_parser.py Config.ebnf

Чтобы преобразовать stdin в json, используя сгенерированный модуль config_parser:

#!/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)

выход

{
  "block1": {
    "othervalue": 32423432,
    "some_value": "some other kind of data",
    "value": "data"
  },
  "block2": {
    "othervalue": 32423432,
    "some_value": "some other kind of data",
    "value": "data"
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...