лучший способ разобрать строку в python в словарь - PullRequest
1 голос
/ 29 октября 2009

У меня есть файл со строками вроде

account = "TEST1" Qty=100 price = 20.11 subject="some value" values="3=this, 4=that"

Специального разделителя не существует, и каждый ключ имеет значение, заключенное в двойные кавычки, если это строка, но не если это число. Нет ключа без значения, хотя могут существовать пустые строки, которые представлены как "", и для кавычки не существует escape-символа, так как он не нужен

Я хочу знать, как правильно проанализировать строку такого типа с python и сохранить значения в виде пар ключ-значение в словаре

Ответы [ 4 ]

11 голосов
/ 29 октября 2009

Нам понадобится регулярное выражение для этого.

import re, decimal
r= re.compile('([^ =]+) *= *("[^"]*"|[^ ]*)')

d= {}
for k, v in r.findall(line):
    if v[:1]=='"':
        d[k]= v[1:-1]
    else:
        d[k]= decimal.Decimal(v)

>>> d
{'account': 'TEST1', 'subject': 'some value', 'values': '3=this, 4=that', 'price': Decimal('20.11'), 'Qty': Decimal('100.0')}

Вы можете использовать число с плавающей запятой вместо десятичной, если хотите, но, вероятно, это плохая идея, если в нее вовлечены деньги.

5 голосов
/ 30 октября 2009

Может быть, немного проще следовать - pyparsing представление:

from pyparsing import *

# define basic elements - use re's for numerics, faster than easier than 
# composing from pyparsing objects
integer = Regex(r'[+-]?\d+')
real = Regex(r'[+-]?\d+\.\d*')
ident = Word(alphanums)
value = real | integer | quotedString.setParseAction(removeQuotes)

# define a key-value pair, and a configline as one or more of these
# wrap configline in a Dict so that results are accessible by given keys
kvpair = Group(ident + Suppress('=') + value)
configline = Dict(OneOrMore(kvpair))

src = 'account = "TEST1" Qty=100 price = 20.11 subject="some value" ' \
        'values="3=this, 4=that"'

configitems = configline.parseString(src)

Теперь вы можете получить доступ к своим частям, используя возвращенный объект конфигурации ParseResults:

>>> print configitems.asList()
[['account', 'TEST1'], ['Qty', '100'], ['price', '20.11'], 
 ['subject', 'some value'], ['values', '3=this, 4=that']]

>>> print configitems.asDict()
{'account': 'TEST1', 'Qty': '100', 'values': '3=this, 4=that', 
  'price': '20.11', 'subject': 'some value'}

>>> print configitems.dump()
[['account', 'TEST1'], ['Qty', '100'], ['price', '20.11'], 
 ['subject', 'some value'], ['values', '3=this, 4=that']]
- Qty: 100
- account: TEST1
- price: 20.11
- subject: some value
- values: 3=this, 4=that

>>> print configitems.keys()
['account', 'subject', 'values', 'price', 'Qty']

>>> print configitems.subject
some value
0 голосов
/ 29 октября 2009

Если вы не хотите использовать регулярное выражение, другой вариант - просто прочитать строку символ за раз:

string = 'account = "TEST1" Qty=100 price = 20.11 subject="some value" values="3=this, 4=that"'

inside_quotes = False
key = None
value = ""
dict = {}

for c in string:
    if c == '"':
        inside_quotes = not inside_quotes
    elif c == '=' and not inside_quotes:
        key = value
        value = ''
    elif c == ' ':
        if inside_quotes:
            value += ' ';
        elif key and value:
            dict[key] = value
            key = None
            value = ''
    else:
        value += c

dict[key] = value
print dict
0 голосов
/ 29 октября 2009

Рекурсивное изменение значений парсинга Бобинса со встроенными равными в качестве словарей:

>>> import re
>>> import pprint
>>>
>>> def parse_line(line):
...     d = {}
...     a = re.compile(r'\s*(\w+)\s*=\s*("[^"]*"|[^ ,]*),?')
...     float_re = re.compile(r'^\d.+$')
...     int_re = re.compile(r'^\d+$')
...     for k,v in a.findall(line):
...             if int_re.match(k):
...                     k = int(k)
...             if v[-1] == '"':
...                     v = v[1:-1]
...             if '=' in v:
...                     d[k] = parse_line(v)
...             elif int_re.match(v):
...                     d[k] = int(v)
...             elif float_re.match(v):
...                     d[k] = float(v)
...             else:
...                     d[k] = v
...     return d
...
>>> line = 'account = "TEST1" Qty=100 price = 20.11 subject="some value" values=
"3=this, 4=that"'
>>> pprint.pprint(parse_line(line))
{'Qty': 100,
 'account': 'TEST1',
 'price': 20.109999999999999,
 'subject': 'some value',
 'values': {3: 'this', 4: 'that'}}
...