Al dividir una cadena vacía en Python, ¿por qué split () devuelve una lista vacía mientras que split ('\ n') devuelve ['']?

Respuestas:

247

Pregunta: Estoy usando split ('\ n') para obtener líneas en una cadena, y descubrí que '' .split () devuelve una lista vacía [], mientras que '' .split ('\ n') devuelve [''] .

El método str.split () tiene dos algoritmos. Si no se dan argumentos, se divide en ejecuciones repetidas de espacios en blanco. Sin embargo, si se proporciona un argumento, se trata como un delimitador único sin ejecuciones repetidas.

En el caso de dividir una cadena vacía, el primer modo (sin argumento) devolverá una lista vacía porque se come el espacio en blanco y no hay valores para poner en la lista de resultados.

Por el contrario, el segundo modo (con un argumento como \n) producirá el primer campo vacío. Considere que si hubiera escrito '\n'.split('\n'), obtendría dos campos (uno dividido, le da dos mitades).

Pregunta: ¿Hay alguna razón específica para tal diferencia?

Este primer modo es útil cuando los datos se alinean en columnas con cantidades variables de espacios en blanco. Por ejemplo:

>>> data = '''\
Shasta      California     14,200
McKinley    Alaska         20,300
Fuji        Japan          12,400
'''
>>> for line in data.splitlines():
        print line.split()

['Shasta', 'California', '14,200']
['McKinley', 'Alaska', '20,300']
['Fuji', 'Japan', '12,400']

El segundo modo es útil para datos delimitados, como CSV, donde las comas repetidas denotan campos vacíos. Por ejemplo:

>>> data = '''\
Guido,BDFL,,Amsterdam
Barry,FLUFL,,USA
Tim,,,USA
'''
>>> for line in data.splitlines():
        print line.split(',')

['Guido', 'BDFL', '', 'Amsterdam']
['Barry', 'FLUFL', '', 'USA']
['Tim', '', '', 'USA']

Tenga en cuenta que el número de campos de resultado es uno mayor que el número de delimitadores. Piensa en cortar una cuerda. Si no haces cortes, tienes una sola pieza. Haciendo un corte, da dos piezas. Haciendo dos cortes, da tres piezas. Y así es con el método str.split (delimitador) de Python :

>>> ''.split(',')       # No cuts
['']
>>> ','.split(',')      # One cut
['', '']
>>> ',,'.split(',')     # Two cuts
['', '', '']

Pregunta: ¿Y hay alguna forma más conveniente de contar líneas en una cadena?

Sí, hay un par de formas fáciles. Uno usa str.count () y el otro usa str.splitlines () . Ambas formas darán la misma respuesta a menos que a la línea final le falte el \n. Si falta la nueva línea final, el enfoque str.splitlines dará la respuesta precisa. Una técnica más rápida que también es precisa utiliza el método de conteo pero luego lo corrige para la nueva línea final:

>>> data = '''\
Line 1
Line 2
Line 3
Line 4'''

>>> data.count('\n')                               # Inaccurate
3
>>> len(data.splitlines())                         # Accurate, but slow
4
>>> data.count('\n') + (not data.endswith('\n'))   # Accurate and fast
4    

Pregunta de @Kaz: ¿Por qué diablos hay dos algoritmos muy diferentes en una sola función?

La firma de str.split tiene aproximadamente 20 años, y varias API de esa época son estrictamente pragmáticas. Si bien no es perfecto, la firma del método tampoco es "terrible". En su mayor parte, las opciones de diseño de API de Guido han resistido la prueba del tiempo.

La API actual no está exenta de ventajas. Considere cadenas como:

ps_aux_header  = "USER               PID  %CPU %MEM      VSZ"
patient_header = "name,age,height,weight"

Cuando se les pide que dividan estas cadenas en campos, las personas tienden a describir ambas utilizando la misma palabra en inglés, "dividir". Cuando se les pide que lean códigos como fields = line.split() o fields = line.split(','), las personas tienden a interpretar correctamente las declaraciones como "divide una línea en campos".

La herramienta de texto a columnas de Microsoft Excel hizo una elección de API similar e incorpora ambos algoritmos de división en la misma herramienta. Las personas parecen modelar mentalmente la división de campos como un concepto único a pesar de que está involucrado más de un algoritmo.

Raymond Hettinger
fuente
28

Parece ser simplemente la forma en que se supone que funciona, de acuerdo con la documentación :

Dividir una cadena vacía con un separador específico devuelve [''].

Si sep no se especifica o es None, se aplica un algoritmo de división diferente: las ejecuciones de espacios en blanco consecutivos se consideran como un solo separador, y el resultado no contendrá cadenas vacías al inicio o al final si la cadena tiene espacios en blanco iniciales o finales. En consecuencia, dividir una cadena vacía o una cadena que consta de solo espacios en blanco con un separador None devuelve [].

Entonces, para que quede más claro, la split()función implementa dos algoritmos de división diferentes y usa la presencia de un argumento para decidir cuál ejecutar. Esto podría deberse a que permite optimizar el que no tiene argumentos más que el que tiene argumentos; No lo sé.

relajarse
fuente
4

.split()sin parámetros intenta ser inteligente. Se divide en cualquier espacio en blanco, tabulaciones, espacios, avances de línea, etc., y también omite todas las cadenas vacías como resultado de esto.

>>> "  fii    fbar \n bopp ".split()
['fii', 'fbar', 'bopp']

Esencialmente, los .split()parámetros sin parámetros se usan para extraer palabras de una cadena, a diferencia de los .split()parámetros que simplemente toman una cadena y la dividen.

Esa es la razón de la diferencia.

Y sí, contar líneas dividiendo no es una manera eficiente. Cuente el número de saltos de línea y agregue uno si la cadena no termina con un salto de línea.

Lennart Regebro
fuente
2

Uso count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1
Gareth Webber
fuente
44
El + 1 solo debe hacerse si el texto no termina con '\ n'.
Lennart Regebro
8
Bueno, si termina con "\ n", entonces la última línea es una línea vacía. Aunque inútil, todavía cuenta como línea, ¿no?
Jakub M.
2
No. cuando escribo 3 líneas de texto en un archivo y finalizo cada una de ellas con un salto de línea, entonces diría que el archivo contiene 3 líneas. en Unix, es una buena práctica tener un archivo de texto que siempre termine con un salto de línea. de lo contrario, cat fileconfunde su línea de comando y Subversion se queja. vi siempre agrega uno.
user829755
2
>>> print str.split.__doc__
S.split([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the
delimiter string.  If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator and empty strings are removed
from the result.

Tenga en cuenta la última oración.

Para contar líneas, simplemente puede contar cuántas \nhay:

line_count = some_string.count('\n') + some_string[-1] != '\n'

La última parte tiene en cuenta la última línea que no termina con \n, aunque esto significa que Hello, World!y Hello, World!\ntiene el mismo recuento de líneas (que para mí es razonable), de lo contrario, simplemente puede agregar 1al recuento de \n.

Bakuriu
fuente
0

Para contar líneas, puede contar el número de saltos de línea:

n_lines = sum(1 for s in the_string if s == "\n") + 1 # add 1 for last line

Editar :

La otra respuesta con incorporado countes más adecuada, en realidad

Jakub M.
fuente
3
Además de solo usar count, los bools son añadibles (de hecho, subclasifican int), por lo que el genexp se puede escribir como sum(s == "\n" for s in the_string).
lvc
¿Ahora solo estás contando líneas vacías?
Thijs van Dien
Sí, no descarto ninguna línea vacía
Jakub M.