¿Cómo puedo analizar una cadena delimitada por comas en una lista (advertencia)?
Pregunta
Necesito poder tomar una cadena como:
'''foo, bar, "one, two", three four'''
en:
['foo', 'bar', 'one, two', 'three four']
Tengo la sensación (con sugerencias de #python) de que la solución involucrará el módulo shlex.
Solución
La solución del módulo shlex permite comillas escapadas, una cita escapa a otra y todo lo sofisticado que admite el shell.
>>> import shlex
>>> my_splitter = shlex.shlex('''foo, bar, "one, two", three four''', posix=True)
>>> my_splitter.whitespace += ','
>>> my_splitter.whitespace_split = True
>>> print list(my_splitter)
['foo', 'bar', 'one, two', 'three', 'four']
Ejemplo de comillas escapadas:
>>> my_splitter = shlex.shlex('''"test, a",'foo,bar",baz',bar \xc3\xa4 baz''',
posix=True)
>>> my_splitter.whitespace = ',' ; my_splitter.whitespace_split = True
>>> print list(my_splitter)
['test, a', 'foo,bar",baz', 'bar \xc3\xa4 baz']
Otros consejos
Depende de lo complicado que quieras ponerte...¿Quieres permitir más de un tipo de cotización?¿Qué tal las comillas escapadas?
Su sintaxis se parece mucho al formato de archivo CSV común, que es compatible con la biblioteca estándar de Python:
import csv
reader = csv.reader(['''foo, bar, "one, two", three four'''], skipinitialspace=True)
for r in reader:
print r
Salidas:
['foo', 'bar', 'one, two', 'three four']
¡HH!
Quizás también quieras considerar el csv módulo.No lo he probado, pero parece que sus datos de entrada están más cerca de CSV que de la sintaxis de Shell (que es lo que analiza shlex).
Podrías hacer algo como esto:
>>> import re
>>> pattern = re.compile(r'\s*("[^"]*"|.*?)\s*,')
>>> def split(line):
... return [x[1:-1] if x[:1] == x[-1:] == '"' else x
... for x in pattern.findall(line.rstrip(',') + ',')]
...
>>> split("foo, bar, baz")
['foo', 'bar', 'baz']
>>> split('foo, bar, baz, "blub blah"')
['foo', 'bar', 'baz', 'blub blah']
Yo diría que lo que estás buscando aquí sería una expresión regular, aunque no estoy muy familiarizado con el motor Regex de Python.
Suponiendo que utiliza coincidencias diferidas, puede obtener un conjunto de coincidencias en una cadena que puede colocar en su matriz.
Si no es necesario que sea bonito, esto podría ayudarte:
def f(s, splitifeven):
if splitifeven & 1:
return [s]
return [x.strip() for x in s.split(",") if x.strip() != '']
ss = 'foo, bar, "one, two", three four'
print sum([f(s, sie) for sie, s in enumerate(ss.split('"'))], [])