Pregunta ¿Cómo puedo dividir una cadena de manera confiable en Python?


En Perl puedo hacer:

my ($x, $y) = split /:/, $str;

Y funcionará independientemente de que la cadena contenga el patrón.

En Python, sin embargo, esto no funcionará:

a, b = "foo".split(":")  # ValueError: not enough values to unpack

¿Cuál es la forma canónica de prevenir errores en tales casos?


73
2017-07-01 15:50


origen


Respuestas:


Si te estás dividiendo en solo dos partes (como en tu ejemplo), puedes usar str.partition() para obtener un argumento de desempaquetado garantizado de 3:

>>> a, sep, b = "foo".partition(":")
>>> a, sep, b
('foo', '', '')

str.partition() siempre devuelve un 3-tuple, si el separador se encuentra o no.

Otra alternativa para Python 3 es utilizar el desempaquetado extendido, como se describe en @ respuesta de cdarke:

>>> a, *b = "foo".split(":")
>>> a, b
('foo', [])

Esto asigna el primer elemento dividido a a y la lista de elementos restantes (si corresponde) para b.


107
2017-07-01 15:52



Como estás en Python 3, es fácil. PEP 3132 introdujo una simplificación bienvenida de la sintaxis al asignar a tuplas - Desempaquetado iterable extendido. En el pasado, si se asignan variables en una tupla, el número de elementos a la izquierda de la asignación debe ser exactamente igual al de la derecha.

En Python 3 podemos designar cualquier variable a la izquierda como una lista prefijando con un asterisco *. Eso obtendrá tantos valores como sea posible, al mismo tiempo que rellene las variables a su derecha (por lo que no tiene que ser el elemento más a la derecha). Esto evita muchas rebanadas desagradables cuando no conocemos la longitud de una tupla.

a, *b = "foo".split(":")  
print("a:", a, "b:", b)

Da:

a: foo b: []

EDITAR después de comentarios y discusión:

En comparación con la versión de Perl, esto es bastante diferente, pero es la forma de Python (3). En comparación con la versión de Perl, re.split() sería más similar, sin embargo, invocar el motor de RE para dividir alrededor de un solo carácter es una sobrecarga innecesaria.

Con múltiples elementos en Python:

s = 'hello:world:sailor'
a, *b = s.split(":")
print("a:", a, "b:", b)

da:

a: hello b: ['world', 'sailor']

Sin embargo, en Perl:

my $s = 'hello:world:sailor';
my ($a, $b) = split /:/, $s;
print "a: $a b: $b\n";

da:

a: hello b: world

Se puede ver que los elementos adicionales se ignoran o pierden en Perl. Eso es bastante fácil de replicar en Python si es necesario:

s = 'hello:world:sailor'
a, *b = s.split(":")
b = b[0]
print("a:", a, "b:", b)

Asi que, a, *b = s.split(":") equivalente en Perl sería

my ($a, @b) = split /:/, $s;

NB: no deberíamos usar $a y $b en general Perl ya que tienen un significado especial cuando se usa con sort. Los he usado aquí por coherencia con el ejemplo de Python.

Python tiene un truco extra en la manga, podemos descomprimirlo en cualquier elemento de la tupla de la izquierda:

s = "one:two:three:four"
a, *b, c = s.split(':')
print("a:", a, "b:", b, "c:", c)

Da:

a: one b: ['two', 'three'] c: four

Mientras que en el equivalente de Perl, la matriz (@b) es codicioso, y el escalar $c es undef:

use strict;
use warnings;

my $s = 'one:two:three:four';
my ($a, @b, $c) = split /:/, $s;
print "a: $a b: @b c: $c\n";

Da:

Use of uninitialized value $c in concatenation (.) or string at gash.pl line 8.
a: one b: two three four c: 

60
2017-07-01 16:01



Usted siempre es libre de atrapar la excepción.

Por ejemplo:

some_string = "foo"

try:
    a, b = some_string.split(":")
except ValueError:
    a = some_string
    b = ""

Si asigna la cadena original completa a a y una cadena vacía para b es el comportamiento deseado, probablemente usaría str.partition() como eugene y sugiere. Sin embargo, esta solución le da más control sobre exactamente lo que sucede cuando no hay separador en la cadena, lo que podría ser útil en algunos casos.


22
2017-07-01 17:09



split siempre devolverá una lista. a, b = ... siempre esperará que la longitud de la lista sea dos. Puedes usar algo como l = string.split(':'); a = l[0]; ....

Aquí hay un trazador de líneas: a, b = (string.split(':') + [None]*2)[:2]


17
2017-07-01 16:12



¿Qué tal el uso de expresiones regulares?

import re 
string = 'one:two:three:four'

en 3.X:

a, *b = re.split(':', string)

en 2.X:

a, b = re.split(':', string)[0], re.split(':', string)[1:]

De esta forma también puedes usar expresiones regulares para dividir (i. E. \ D)


3
2017-07-03 06:12