ultimo tamaño separar por poner palabras leer espacios espacio caracteres caracter python regex

separar - tamaño string python



Dividir una cadena por espacios-preservando las subcadenas entre comillas-en Python (16)

Tengo una cadena que es así:

this is "a test"

Estoy tratando de escribir algo en Python para dividirlo por espacio mientras ignoro los espacios entre comillas. El resultado que estoy buscando es:

[''this'',''is'',''a test'']

PD. Sé que va a preguntar "qué sucede si hay comillas dentro de las citas, bueno, en mi solicitud, eso nunca sucederá.


Como esta pregunta está etiquetada con expresiones regulares, decidí probar un enfoque de expresiones regulares. Primero reemplacé todos los espacios en las partes de comillas con / x00, luego dividí por espacios, luego reemplacé / x00 de nuevo a espacios en cada parte.

Ambas versiones hacen lo mismo, pero splitter es un poco más legible que splitter2.

import re s = ''this is "a test" some text "another test"'' def splitter(s): def replacer(m): return m.group(0).replace(" ", "/x00") parts = re.sub(''".+?"'', replacer, s).split() parts = [p.replace("/x00", " ") for p in parts] return parts def splitter2(s): return [p.replace("/x00", " ") for p in re.sub(''".+?"'', lambda m: m.group(0).replace(" ", "/x00"), s).split()] print splitter2(s)


Dependiendo de su caso de uso, es posible que también desee revisar el módulo csv:

import csv lines = [''this is "a string"'', ''and more "stuff"''] for row in csv.reader(lines, delimiter=" "): print row

Salida:

[''this'', ''is'', ''a string''] [''and'', ''more'', ''stuff'']


Eche un vistazo al módulo shlex , particularmente a shlex.split .

>>> import shlex >>> shlex.split(''This is "a test"'') [''This'', ''is'', ''a test'']


El principal problema con el enfoque shlex aceptado es que no ignora los caracteres de escape que se encuentran fuera de las subcadenas citadas, y da resultados ligeramente inesperados en algunos casos de esquina.

Tengo el siguiente caso de uso, donde necesito una función de división que divide las cadenas de entrada de manera que se conserven las subcadenas entre comillas simples o entre comillas dobles, con la capacidad de evitar las comillas dentro de dicha subcadena. Las citas dentro de una cadena sin comillas no deben tratarse de manera diferente a cualquier otro carácter. Algunos ejemplos de casos de prueba con el resultado esperado:

input string | expected output =============================================== ''abc def'' | [''abc'', ''def''] "abc //s def" | [''abc'', ''//s'', ''def''] ''"abc def" ghi'' | [''abc def'', ''ghi''] "''abc def'' ghi" | [''abc def'', ''ghi''] ''"abc //" def" ghi'' | [''abc " def'', ''ghi''] "''abc //' def'' ghi" | ["abc '' def", ''ghi''] "''abc //s def'' ghi" | [''abc //s def'', ''ghi''] ''"abc //s def" ghi'' | [''abc //s def'', ''ghi''] ''"" test'' | ['''', ''test''] "'''' test" | ['''', ''test''] "abc''def" | ["abc''def"] "abc''def''" | ["abc''def''"] "abc''def'' ghi" | ["abc''def''", ''ghi''] "abc''def''ghi" | ["abc''def''ghi"] ''abc"def'' | [''abc"def''] ''abc"def"'' | [''abc"def"''] ''abc"def" ghi'' | [''abc"def"'', ''ghi''] ''abc"def"ghi'' | [''abc"def"ghi''] "r''AA'' r''.*_xyz$''" | ["r''AA''", "r''.*_xyz$''"]

Terminé con la siguiente función para dividir una cadena de manera que los resultados esperados resulten para todas las cadenas de entrada:

import re def quoted_split(s): def strip_quotes(s): if s and (s[0] == ''"'' or s[0] == "''") and s[0] == s[-1]: return s[1:-1] return s return [strip_quotes(p).replace(''//"'', ''"'').replace("//'", "''") / for p in re.findall(r''"(?://.|[^"])*"|/'(?://.|[^/'])*/'|[^/s]+'', s)]

La siguiente aplicación de prueba verifica los resultados de otros enfoques ( shlex y csv por ahora) y la implementación dividida personalizada:

#!/bin/python2.7 import csv import re import shlex from timeit import timeit def test_case(fn, s, expected): try: if fn(s) == expected: print ''[ OK ] %s -> %s'' % (s, fn(s)) else: print ''[FAIL] %s -> %s'' % (s, fn(s)) except Exception as e: print ''[FAIL] %s -> exception: %s'' % (s, e) def test_case_no_output(fn, s, expected): try: fn(s) except: pass def test_split(fn, test_case_fn=test_case): test_case_fn(fn, ''abc def'', [''abc'', ''def'']) test_case_fn(fn, "abc //s def", [''abc'', ''//s'', ''def'']) test_case_fn(fn, ''"abc def" ghi'', [''abc def'', ''ghi'']) test_case_fn(fn, "''abc def'' ghi", [''abc def'', ''ghi'']) test_case_fn(fn, ''"abc //" def" ghi'', [''abc " def'', ''ghi'']) test_case_fn(fn, "''abc //' def'' ghi", ["abc '' def", ''ghi'']) test_case_fn(fn, "''abc //s def'' ghi", [''abc //s def'', ''ghi'']) test_case_fn(fn, ''"abc //s def" ghi'', [''abc //s def'', ''ghi'']) test_case_fn(fn, ''"" test'', ['''', ''test'']) test_case_fn(fn, "'''' test", ['''', ''test'']) test_case_fn(fn, "abc''def", ["abc''def"]) test_case_fn(fn, "abc''def''", ["abc''def''"]) test_case_fn(fn, "abc''def'' ghi", ["abc''def''", ''ghi'']) test_case_fn(fn, "abc''def''ghi", ["abc''def''ghi"]) test_case_fn(fn, ''abc"def'', [''abc"def'']) test_case_fn(fn, ''abc"def"'', [''abc"def"'']) test_case_fn(fn, ''abc"def" ghi'', [''abc"def"'', ''ghi'']) test_case_fn(fn, ''abc"def"ghi'', [''abc"def"ghi'']) test_case_fn(fn, "r''AA'' r''.*_xyz$''", ["r''AA''", "r''.*_xyz$''"]) def csv_split(s): return list(csv.reader([s], delimiter='' ''))[0] def re_split(s): def strip_quotes(s): if s and (s[0] == ''"'' or s[0] == "''") and s[0] == s[-1]: return s[1:-1] return s return [strip_quotes(p).replace(''//"'', ''"'').replace("//'", "''") for p in re.findall(r''"(?://.|[^"])*"|/'(?://.|[^/'])*/'|[^/s]+'', s)] if __name__ == ''__main__'': print ''shlex/n'' test_split(shlex.split) print print ''csv/n'' test_split(csv_split) print print ''re/n'' test_split(re_split) print iterations = 100 setup = ''from __main__ import test_split, test_case_no_output, csv_split, re_split/nimport shlex, re'' def benchmark(method, code): print ''%s: %.3fms per iteration'' % (method, (1000 * timeit(code, setup=setup, number=iterations) / iterations)) benchmark(''shlex'', ''test_split(shlex.split, test_case_no_output)'') benchmark(''csv'', ''test_split(csv_split, test_case_no_output)'') benchmark(''re'', ''test_split(re_split, test_case_no_output)'')

Salida:

shlex [ OK ] abc def -> [''abc'', ''def''] [FAIL] abc /s def -> [''abc'', ''s'', ''def''] [ OK ] "abc def" ghi -> [''abc def'', ''ghi''] [ OK ] ''abc def'' ghi -> [''abc def'', ''ghi''] [ OK ] "abc /" def" ghi -> [''abc " def'', ''ghi''] [FAIL] ''abc /' def'' ghi -> exception: No closing quotation [ OK ] ''abc /s def'' ghi -> [''abc //s def'', ''ghi''] [ OK ] "abc /s def" ghi -> [''abc //s def'', ''ghi''] [ OK ] "" test -> ['''', ''test''] [ OK ] '''' test -> ['''', ''test''] [FAIL] abc''def -> exception: No closing quotation [FAIL] abc''def'' -> [''abcdef''] [FAIL] abc''def'' ghi -> [''abcdef'', ''ghi''] [FAIL] abc''def''ghi -> [''abcdefghi''] [FAIL] abc"def -> exception: No closing quotation [FAIL] abc"def" -> [''abcdef''] [FAIL] abc"def" ghi -> [''abcdef'', ''ghi''] [FAIL] abc"def"ghi -> [''abcdefghi''] [FAIL] r''AA'' r''.*_xyz$'' -> [''rAA'', ''r.*_xyz$''] csv [ OK ] abc def -> [''abc'', ''def''] [ OK ] abc /s def -> [''abc'', ''//s'', ''def''] [ OK ] "abc def" ghi -> [''abc def'', ''ghi''] [FAIL] ''abc def'' ghi -> ["''abc", "def''", ''ghi''] [FAIL] "abc /" def" ghi -> [''abc //', ''def"'', ''ghi''] [FAIL] ''abc /' def'' ghi -> ["''abc", "//'", "def''", ''ghi''] [FAIL] ''abc /s def'' ghi -> ["''abc", ''//s'', "def''", ''ghi''] [ OK ] "abc /s def" ghi -> [''abc //s def'', ''ghi''] [ OK ] "" test -> ['''', ''test''] [FAIL] '''' test -> ["''''", ''test''] [ OK ] abc''def -> ["abc''def"] [ OK ] abc''def'' -> ["abc''def''"] [ OK ] abc''def'' ghi -> ["abc''def''", ''ghi''] [ OK ] abc''def''ghi -> ["abc''def''ghi"] [ OK ] abc"def -> [''abc"def''] [ OK ] abc"def" -> [''abc"def"''] [ OK ] abc"def" ghi -> [''abc"def"'', ''ghi''] [ OK ] abc"def"ghi -> [''abc"def"ghi''] [ OK ] r''AA'' r''.*_xyz$'' -> ["r''AA''", "r''.*_xyz$''"] re [ OK ] abc def -> [''abc'', ''def''] [ OK ] abc /s def -> [''abc'', ''//s'', ''def''] [ OK ] "abc def" ghi -> [''abc def'', ''ghi''] [ OK ] ''abc def'' ghi -> [''abc def'', ''ghi''] [ OK ] "abc /" def" ghi -> [''abc " def'', ''ghi''] [ OK ] ''abc /' def'' ghi -> ["abc '' def", ''ghi''] [ OK ] ''abc /s def'' ghi -> [''abc //s def'', ''ghi''] [ OK ] "abc /s def" ghi -> [''abc //s def'', ''ghi''] [ OK ] "" test -> ['''', ''test''] [ OK ] '''' test -> ['''', ''test''] [ OK ] abc''def -> ["abc''def"] [ OK ] abc''def'' -> ["abc''def''"] [ OK ] abc''def'' ghi -> ["abc''def''", ''ghi''] [ OK ] abc''def''ghi -> ["abc''def''ghi"] [ OK ] abc"def -> [''abc"def''] [ OK ] abc"def" -> [''abc"def"''] [ OK ] abc"def" ghi -> [''abc"def"'', ''ghi''] [ OK ] abc"def"ghi -> [''abc"def"ghi''] [ OK ] r''AA'' r''.*_xyz$'' -> ["r''AA''", "r''.*_xyz$''"] shlex: 0.281ms per iteration csv: 0.030ms per iteration re: 0.049ms per iteration

Por lo tanto, el rendimiento es mucho mejor que shlex , y puede mejorarse aún más al precompilar la expresión regular, en cuyo caso superará el enfoque de csv .


Hmm, parece que no puede encontrar el botón "Responder" ... de todos modos, esta respuesta se basa en el enfoque de Kate, pero divide correctamente las cadenas con subcadenas que contienen comillas escapadas y también elimina las comillas de inicio y final de las subcadenas:

[i.strip(''"'').strip("''") for i in re.split(r''(/s+|(?<!//)".*?(?<!//)"|(?<!//)/'.*?(?<!//)/')'', string) if i.strip()]

Esto funciona en cadenas como ''This is " a ///"test///"///'s substring"'' (desafortunadamente, el marcado demente es necesario para evitar que Python elimine los escapes).

Si no se desean los escapes resultantes en las cadenas en la lista devuelta, puede usar esta versión ligeramente alterada de la función:

[i.strip(''"'').strip("''").decode(''string_escape'') for i in re.split(r''(/s+|(?<!//)".*?(?<!//)"|(?<!//)/'.*?(?<!//)/')'', string) if i.strip()]


Los problemas de Unicode con shlex discutidos anteriormente (respuesta principal) parecen resolverse (indirectamente) en 2.7.2+ según http://bugs.python.org/issue6988#msg146200

(respuesta separada porque no puedo comentar)


Para preservar las cotizaciones use esta función:

def getArgs(s): args = [] cur = '''' inQuotes = 0 for char in s.strip(): if char == '' '' and not inQuotes: args.append(cur) cur = '''' elif char == ''"'' and not inQuotes: inQuotes = 1 cur += char elif char == ''"'' and inQuotes: inQuotes = 0 cur += char else: cur += char args.append(cur) return args


Para solucionar los problemas de Unicode en algunas versiones de Python 2, sugiero:

from shlex import split as _split split = lambda a: [b.decode(''utf-8'') for b in _split(a.encode(''utf-8''))]


Parece que por razones de rendimiento re es más rápido. Aquí está mi solución utilizando un operador menos codicioso que conserva las citas externas:

re.findall("(?:/".*?/"|/S)+", s)

Resultado:

[''this'', ''is'', ''"a test"'']

Deja constructos como aaa"bla blub"bbb juntos, ya que estos tokens no están separados por espacios. Si la cadena contiene caracteres escapados, puedes hacer coincidir así:

>>> a = "She said /"He said, ///"My name is Mark.///"/"" >>> a ''She said "He said, //"My name is Mark.//""'' >>> for i in re.findall("(?:/".*?[^////]/"|/S)+", a): print(i) ... She said "He said, /"My name is Mark./""

Tenga en cuenta que esto también coincide con la cadena vacía "" por medio de la parte /S del patrón.


Prueba de velocidad de diferentes respuestas:

import re import shlex import csv line = ''this is "a test"'' %timeit [p for p in re.split("( |///".*?///"|''.*?'')", line) if p.strip()] 100000 loops, best of 3: 5.17 µs per loop %timeit re.findall(r''[^"/s]/S*|".+?"'', line) 100000 loops, best of 3: 2.88 µs per loop %timeit list(csv.reader([line], delimiter=" ")) The slowest run took 9.62 times longer than the fastest. This could mean that an intermediate result is being cached. 100000 loops, best of 3: 2.4 µs per loop %timeit shlex.split(line) 10000 loops, best of 3: 50.2 µs per loop


Prueba esto:

def adamsplit(s): result = [] inquotes = False for substring in s.split(''"''): if not inquotes: result.extend(substring.split()) else: result.append(substring) inquotes = not inquotes return result

Algunas cadenas de prueba:

''This is "a test"'' -> [''This'', ''is'', ''a test''] ''"This is /'a test/'"'' -> ["This is ''a test''"]


Quieres split, desde el módulo shlex .

>>> import shlex >>> shlex.split(''this is "a test"'') [''this'', ''is'', ''a test'']

Esto debería hacer exactamente lo que quieres.


Si no te interesan las cadenas de sub que un simple

>>> ''a short sized string with spaces ''.split()

Actuación:

>>> s = " (''a short sized string with spaces ''*100).split() " >>> t = timeit.Timer(stmt=s) >>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 171.39 usec/pass

O módulo de cadena

>>> from string import split as stringsplit; >>> stringsplit(''a short sized string with spaces ''*100)

Rendimiento: el módulo de cadena parece funcionar mejor que los métodos de cadena

>>> s = "stringsplit(''a short sized string with spaces ''*100)" >>> t = timeit.Timer(s, "from string import split as stringsplit") >>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 154.88 usec/pass

O puedes usar el motor RE

>>> from re import split as resplit >>> regex = ''/s+'' >>> medstring = ''a short sized string with spaces ''*100 >>> resplit(regex, medstring)

Actuación

>>> s = "resplit(regex, medstring)" >>> t = timeit.Timer(s, "from re import split as resplit; regex=''/s+''; medstring=''a short sized string with spaces ''*100") >>> print "%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000) 540.21 usec/pass

Para cadenas muy largas, no debe cargar toda la cadena en la memoria y en su lugar dividir las líneas o utilizar un bucle iterativo


Utilizo shlex.split para procesar 70,000,000 de líneas de registro de calamar, es muy lento. Así que me cambié a re.

Por favor, intente esto, si tiene problemas de rendimiento con shlex.

import re def line_split(line): return re.findall(r''[^"/s]/S*|".+?"'', line)


Veo enfoques de expresiones regulares aquí que parecen complejos y / o incorrectos. Esto me sorprende, ya que la sintaxis de expresiones regulares puede describir fácilmente "espacios en blanco o comillas entre comillas", y la mayoría de los motores de expresiones regulares (incluido el de Python) se pueden dividir en una expresión regular. Entonces, si vas a usar expresiones regulares, ¿por qué no simplemente decir exactamente lo que quieres decir?

test = ''this is "a test"'' # or "this is ''a test''" # pieces = [p for p in re.split("( |[///"''].*[///"''])", test) if p.strip()] # From comments, use this: pieces = [p for p in re.split("( |///".*?///"|''.*?'')", test) if p.strip()]

Explicación:

[///"''] = double-quote or single-quote .* = anything ( |X) = space or X .strip() = remove space and empty-string separators

shlex probablemente proporciona más características, sin embargo.


Yo sugiero:

cadena de prueba:

s = ''abc "ad" /'fg/' "kk/'rdt/'" zzz"34"zzz "" /'/'''

para capturar también "" y '''':

import re re.findall(r''"[^"]*"|/'[^/']*/'|[^"/'/s]+'',s)

resultado:

[''abc'', ''"ad"'', "''fg''", ''"kk/'rdt/'"'', ''zzz'', ''"34"'', ''zzz'', ''""'', "''''"]

para ignorar "" y "''vacíos:

import re re.findall(r''"[^"]+"|/'[^/']+/'|[^"/'/s]+'',s)

resultado:

[''abc'', ''"ad"'', "''fg''", ''"kk/'rdt/'"'', ''zzz'', ''"34"'', ''zzz'']