Por ejemplo, quiero dividir

str = '"a,b,c",d,e,f'

En

["a,b,c",'d','e','f']

(es decir, no divida la parte citada) En este caso, esto se puede hacer con

re.findall('".*?"|[^,]+',str)

Sin embargo, si

str = '"a,,b,c",d,,f'

Quiero

["a,,b,c",'d','','f']

Es decir, quiero un comportamiento que sea como la función dividida de Python. ¿Hay alguna manera de hacer esto en una línea (pequeña), posiblemente usando la biblioteca de Python?

En realidad, me acabo de dar cuenta (en este sitio) de que el módulo csv es perfecto para lo que quiero hacer, pero tengo curiosidad por saber si hay una expresión regular que pueda usar para hacerlo también.

2
Alex 19 jun. 2009 a las 23:51

6 respuestas

La mejor respuesta
re.split(',(?=(?:[^"]*"[^"]*")*[^"]*$)', str)

Después de hacer coincidir una coma, si hay un número impar de comillas más adelante, la coma debe estar dentro de un par de comillas, por lo que no cuenta como un delimitador. Obviamente, esto no tiene en cuenta la posibilidad de que se escapen las comillas, pero eso se puede manejar si es necesario, solo hace que la expresión regular sea el doble de fea de lo que ya es. :RE

2
Alan Moore 20 jun. 2009 a las 12:14

Aquí hay una función que realizará la tarea:

def smart_split(data, delimiter=","):
    """ Performs splitting with string preservation. This reads both single and
        double quoted strings.
    """
    result = []
    quote_type = None
    buffer = ""
    position = 0
    while position < len(data):
        if data[position] in ["\"", "'"]:
            quote_type = data[position]
            while quote_type is not None:
                position += 1
                if data[position] == quote_type:
                    quote_type = None
                    position += 1
                else:
                    buffer += data[position]
        if data[position] == delimiter:
            result.append(buffer)
            buffer = ""
        else:
            buffer += data[position]
        position += 1
    result.append(buffer)
    return result

Ejemplo de uso:

str = '"a,b,c",d,e,f'
print smart_split(str)
# Prints: ['a,b,c', 'd', 'e', 'f']
0
Evan Fosmark 19 jun. 2009 a las 22:27

Escribir una máquina de estados para esto, por otro lado, parece ser bastante sencillo. Los DFA y las expresiones regulares tienen el mismo poder, pero generalmente uno de ellos es más adecuado para el problema en cuestión, y generalmente depende mucho de la lógica adicional que pueda necesitar implementar.

1
oggy 19 jun. 2009 a las 20:26

Puede acercarse utilizando especificadores no codiciosos. Lo más cercano que tengo es:

>>> re.findall('(".*?"|.*?)(?:,|$)',  '"a,b,c",d,e,f')
['"a,,b,c"', 'd', '', 'f', '']

Pero como ves, terminas con una cadena vacía redundante al final, que no se puede distinguir del resultado que obtienes cuando la cadena termina con una coma:

>>> re.findall('(".*?"|.*?)(?:,|$)', '"a,b,c",d,e,f,')
['"a,,b,c"', 'd', '', 'f', '']

Así que tendrías que hacer algunos ajustes manuales al final, algo como:

matches = regex,findall(s)
if not s.endswith(","): matches.pop()

O

matches = regex.findall(s+",")[:-1]

Probablemente hay una mejor manera.

0
Brian 19 jun. 2009 a las 21:07

Use el módulo csv ya que es un analizador real. Las expresiones regulares son no óptimas (o completamente inadecuadas) para la mayoría de las cosas que involucran delimitadores coincidentes en los que cambian las reglas (no estoy seguro de si esta gramática particular es regular o no). Es posible que pueda crear una expresión regular que funcione en este caso, pero sería bastante compleja (especialmente cuando se trata de casos como "Él dijo, \" Cómo estás \ "").

2
Ben Hughes 19 jun. 2009 a las 20:09

Aquí hay una función realmente corta que hará lo mismo:

def split (aString):
    splitByQuotes = (",%s,"%aString).split('"')
    splitByQuotes[0::2] = [x.split(",")[1:-1] for x in splitByQuotes[0::2]]
    return [a.strip() \
        for b in splitByQuotes \
        for a in (b if type(b)==list else [b])]

Divide la cadena donde están las comillas, creando una lista donde cada elemento par es lo que está fuera de las comillas y cada elemento impar es lo que se encapsuló entre comillas. Lo que está entre comillas deja solo, lo que está afuera se divide donde están las comas. Ahora tenemos una lista de listas y cadenas alternas, que luego desenvolvemos con la última línea. La razón para envolver la cadena en comas al principio y eliminar comas en el medio es para evitar elementos vacíos de repuesto en la lista. Debería poder manejar espacios en blanco: agregué una función strip () al final para que produzca resultados limpios, pero eso no es necesario.

Uso:

>>> print split('c, , "a,,b,c",d,"moo","f"')
['c', '', 'a,,b,c', 'd', 'moo', 'f']
0
Markus 20 jun. 2009 a las 15:38