Pregunta

Tengo un archivo con líneas como

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

No hay un delimitador especial y cada clave tiene un valor rodeado de comillas dobles si es una cadena pero no si es un número. No hay una clave sin un valor, aunque puede haber cadenas en blanco que se representan como " " y no hay caracteres de escape para una cita, ya que no es necesaria

Quiero saber cuál es una buena manera de analizar este tipo de línea con python y almacenar los valores como pares clave-valor en un diccionario

¿Fue útil?

Solución

Vamos a necesitar una expresión regular para esto.

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

Puede usar flotante en lugar de decimal si lo prefiere, pero probablemente sea una mala idea si hay dinero involucrado.

Otros consejos

Quizás un poco más simple de seguir es la pyparsing rendición:

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)

Ahora puede acceder a sus piezas utilizando el objeto ParseResults de configitems devuelto:

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

Una variación recursiva de los valores de análisis de bobince con iguales incrustados como diccionarios:

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

Si no desea utilizar una expresión regular, otra opción es simplemente leer la cadena de caracteres a la vez:

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top