¿Por qué se devuelven cadenas vacías en los resultados de split ()?

120

¿Qué sentido tiene '/segment/segment/'.split('/')volver ['', 'segment', 'segment', '']?

Observe los elementos vacíos. Si está dividiendo en un delimitador que está en la posición uno y al final de una cadena, ¿qué valor adicional le da que la cadena vacía se devuelva desde cada extremo?

orokusaki
fuente
1
Tengo la misma pregunta y la busqué durante mucho tiempo. Ahora entiendo que los resultados vacíos son realmente importantes. Gracias por tu pregunta
emeraldhieu
2
Una solución es usar strip()para quitar los caracteres divididos '/segment/segment/'.strip('/').split('/')
iniciales

Respuestas:

178

str.splitcomplementos str.join, entonces

"/".join(['', 'segment', 'segment', ''])

te devuelve la cadena original.

Si las cadenas vacías no estuvieran allí, la primera y la última '/'faltarían después de lajoin()

John La Rooy
fuente
11
Simple, pero responde completamente a la pregunta.
orokusaki
Me sorprendió descubrir que las comillas rizadas son realmente válidas en Python ... pero, pero ... ¿cómo? Los médicos no parecen mencionar esto.
Tim Pietzcker
@Tim, no tengo idea de cómo llegaron esas citas: /
John La Rooy
7
Entonces, ¿no estás usando Microsoft Word como tu IDE de Python, entonces? :)
Tim Pietzcker
1
@ aaa90210 ¿quién dijo que las respuestas simples no eran las mejores? Fue un comentario (en primer lugar, hace 5 años) sobre cómo la respuesta fue simple, pero respondió completamente a la pregunta. Usar "pero" en una oración no implica nada malo. Una respuesta no simple podría haber sido una respuesta más completa (por ejemplo, incluyendo decisiones relevantes o un PEP relacionado con la funcionalidad indicada).
orokusaki
88

De manera más general, para eliminar las cadenas vacías devueltas en los split()resultados, es posible que desee consultar la filterfunción.

Ejemplo:

filter(None, '/segment/segment/'.split('/'))

devoluciones

['segment', 'segment']
Franck Dernoncourt
fuente
3
Gracias por esto, no sé por qué esta respuesta es tan baja, todo lo demás es rudimentario.
Wedge
6
Si desea recopilar el resultado en una lista en lugar de obtener un objeto de filtro como salida, coloque toda la estructura del filtro list(...).
Tim Visée
29

Hay dos puntos principales a considerar aquí:

  • Esperar que el resultado de '/segment/segment/'.split('/')sea ​​igual a ['segment', 'segment']es razonable, pero luego se pierde información. Si split()funcionó como querías, si te digo eso a.split('/') == ['segment', 'segment'], no puedes decirme qué afue.
  • ¿Cuál debería ser el resultado de 'a//b'.split()ser? ['a', 'b']?, o ['a', '', 'b']? Es decir, ¿deberían split()fusionarse los delimitadores adyacentes? Si fuera así, será muy difícil analizar los datos delimitados por un carácter y algunos de los campos pueden estar vacíos. Estoy bastante seguro de que hay muchas personas que hacen que los valores vacíos en el resultado para el caso anterior!

Al final, se reduce a dos cosas:

Consistencia: si tengo ndelimitadores, en a, obtengo n+1valores después de split().

Debería ser posible hacer cosas complejas y fácil de hacer cosas simples: si desea ignorar las cadenas vacías como resultado del split(), siempre puede hacer:

def mysplit(s, delim=None):
    return [x for x in s.split(delim) if x]

pero si uno no quiere ignorar los valores vacíos, debería poder hacerlo.

El lenguaje tiene que elegir una definición de split(): hay demasiados casos de uso diferentes para satisfacer los requisitos de todos de forma predeterminada. Creo que la elección de Python es buena y es la más lógica. (Aparte, una de las razones por las que no me gustan las C strtok()es porque fusiona delimitadores adyacentes, lo que hace que sea extremadamente difícil realizar un análisis / tokenización seria con ellas).

Hay una excepción: a.split()sin un argumento se exprime el espacio en blanco consecutivo, pero se puede argumentar que esto es lo correcto en ese caso. Si no desea el comportamiento, siempre puede hacerlo a.split(' ').

Alok Singhal
fuente
Para aquellos que se preguntan si es más rápido destruir espacios duplicados y luego dividir, o dividir y solo tomar cadenas no vacías, esto es lo que obtengo: python3 -m timeit "import re ; re.sub(' +', ' foo bar baz ', '').split(' ')"-> 875 nseg por ciclo; python3 -m timeit "[token for token in ' foo bar baz '.split(' ') if token]"-> 616 nsec por bucle
s3cur3
8

Tener x.split(y)siempre devolver una lista de 1 + x.count(y)elementos es una regularidad preciosa - como @ gnibbler ya ha señalado, hace splity joinexactamente inversas entre sí (como obviamente deberían ser), también mapea con precisión la semántica de todo tipo de registros unidos por delimitadores ( como csvlíneas de archivo [[netos de problemas de citas]], líneas de /etc/groupUnix, etc.), permite (como se menciona en la respuesta de @ Roman) comprobaciones sencillas de (por ejemplo) rutas absolutas frente a relativas (en rutas de archivos y URL), Etcétera.

Otra forma de verlo es que no debe tirar información por la ventana sin obtener ganancias. ¿Qué se ganaría al hacer x.split(y)equivalente a x.strip(y).split(y)? Nada, por supuesto - es fácil de usar la segunda forma, cuando eso es lo que quieres decir, pero si la primera forma fue considerada arbitraria en el sentido de la segunda, que tendrían mucho trabajo que hacer cuando no desea que el primero de ellos ( lo cual está lejos de ser raro, como señala el párrafo anterior).

Pero realmente, pensar en términos de regularidad matemática es la forma más simple y general en la que puede aprender a diseñar API aceptables. Para tomar un ejemplo diferente, es muy importante que para cualquier valor válido xy y x == x[:y] + x[y:]- que indica inmediatamente por qué se debe excluir un extremo de un corte . Cuanto más simple sea la afirmación invariante que pueda formular, más probable es que la semántica resultante sea lo que necesita en los usos de la vida real, parte del hecho místico de que las matemáticas son muy útiles para tratar con el universo.

Intente formular el invariante para un splitdialecto en el que los delimitadores iniciales y finales están en mayúsculas especiales ... contraejemplo: métodos de cadena como isspaceno son extremadamente simples, x.isspace()es equivalente a x and all(c in string.whitespace for c in x), esa entrada tonta x andes la razón por la que tan a menudo se encuentra codificando not x or x.isspace(), para volver a la simplicidad que debería haber sido diseñada en los is...métodos de cadena (donde una cadena vacía "es" cualquier cosa que desee, al contrario del sentido del caballo del hombre en la calle, tal vez [[conjuntos vacíos, como cero & c, siempre ha confundido a la mayoría de la gente ;-)]], ¡pero se ajusta completamente al sentido común matemático obvio y refinado ! -).

Alex Martelli
fuente
5

¿No estoy seguro de qué tipo de respuesta estás buscando? Obtienes tres coincidencias porque tienes tres delimitadores. Si no quieres ese vacío, solo usa:

'/segment/segment/'.strip('/').split('/')
Jamieb
fuente
4
-1 porque obtienes cuatro coincidencias, no tres, y esto tampoco responde realmente a la pregunta.
Roman
1
+1 para contrarrestar el negativo. No dijo que obtendrías tres resultados. Dijo "tres coincidencias" por "tres delimitadores", lo que me suena lógico. Sin embargo, no obtiene "cuatro coincidencias" de nada. Sin embargo, obtienes "cuatro elementos" en tu resultado. Además, no responde directamente el "por qué", pero proporciona una forma sencilla de conseguir lo que realmente quiere ... lo que no creo que merezca un voto negativo. Si vas a criticar a alguien (con un voto negativo, nada menos), ¡ten más cuidado! ¡Salud! 8 ^)
kodybrown
@wasatchwizard Gracias por la aclaración. Agradezco la corrección y la recomendación. Desafortunadamente, ahora mi voto está bloqueado y no se puede cambiar.
Roman
Me encanta tu solución: tira y luego divide para eliminar el resultado vacío
Nam G VU
5

Bueno, te permite saber que había un delimitador allí. Entonces, ver 4 resultados le permite saber que tenía 3 delimitadores. Esto le da el poder de hacer lo que quiera con esta información, en lugar de que Python elimine los elementos vacíos y luego haga que usted verifique manualmente los delimitadores de inicio o finalización si necesita saberlo.

Ejemplo simple: digamos que desea verificar los nombres de archivo absolutos frente a los relativos. De esta manera, puede hacerlo todo con la división, sin tener que verificar también cuál es el primer carácter de su nombre de archivo.

romano
fuente
1

Considere este ejemplo mínimo:

>>> '/'.split('/')
['', '']

splitdebe darle lo que está antes y después del delimitador '/', pero no hay otros caracteres. Entonces tiene que darle la cadena vacía, que técnicamente precede y sigue a '/', porque '' + '/' + '' == '/'.

timgeb
fuente