79 votes

Comment diviser de manière fiable une chaîne de caractères en Python, lorsque celle-ci peut ne pas contenir le motif ou tous les éléments n ?

En Perl, je peux faire:

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

Et cela fonctionnera que la chaîne contienne ou non le motif.

En Python, cependant, cela ne fonctionnera pas:

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

Quelle est la manière canonique d'éviter les erreurs dans de tels cas?

4 votes

En Perl, que se passe-t-il si la chaîne ne contient pas le motif ? Est-ce que $x et $y reçoivent la chaîne entière, ou est-ce que $y reçoit null ou quelque chose d'autre ?

6 votes

@Don'tPanic: $x obtient toute la chaîne, $y est undef (qui est similaire à None, mais légèrement différent).

0 votes

Quel est le comportement attendu quand ils contiennent plusieurs occurrences du motif de séparation ? De plus, l'argument de motif dans split de Perl est-il une regex ou une chaîne régulière ?

115voto

eugene y Points 37378

Si vous divisez en juste deux parties (comme dans votre exemple) vous pouvez utiliser str.partition() pour obtenir une taille de déballage d'argument garantie de 3 :

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

str.partition() retourne toujours un tuple de 3 éléments, que le séparateur soit trouvé ou non.

Une autre alternative pour Python 3.x est d'utiliser le déballage itérable étendu :

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

Cela assigne le premier élément divisé à a et la liste des éléments restants (le cas échéant) à b.

60voto

cdarke Points 8020

Comme vous utilisez Python 3, c'est facile. PEP 3132 a introduit une simplification bienvenue de la syntaxe lors de l'assignation à des tuples - Déballage étendu d'itérable. Dans le passé, lors de l'assignation à des variables dans un tuple, le nombre d'éléments à gauche de l'assignation doit être exactement égal à celui de la droite.

En Python 3, nous pouvons désigner n'importe quelle variable à gauche comme une liste en la préfixant d'un astérisque *. Cela récupère autant de valeurs qu'il le peut, tout en peuplant les variables à sa droite (donc ce n'a pas besoin d'être l'élément le plus à droite). Cela évite de nombreuses découpes complexes lorsque nous ne connaissons pas la longueur d'un tuple.

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

Donne :

a: foo b: []

ÉDITER suite aux commentaires et discussions :

En comparaison avec la version Perl, c'est considérablement différent, mais c'est la manière Python (3). Comparé à la version Perl, re.split() serait plus similaire, cependant invoquer le moteur RE pour diviser autour d'un seul caractère est une surcharge inutile.

Avec plusieurs éléments en Python :

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

donne :

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

Cependant en Perl :

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

donne :

a: hello b: world

Il est possible de constater que des éléments supplémentaires sont ignorés ou perdus en Perl. C'est assez facile à reproduire en Python si nécessaire :

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

Ainsi, a, *b = s.split(":") l'équivalent en Perl serait

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

NB : on ne devrait pas utiliser $a et $b en général en Perl car ils ont un sens spécial lorsqu'ils sont utilisés avec sort. Je les ai utilisés ici par cohérence avec l'exemple Python.

Python a aussi un tour supplémentaire dans sa manche, nous pouvons déballer vers n'importe quel élément dans le tuple à gauche :

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

Donne :

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

Tandis que dans l'équivalent Perl, le tableau (@b) est gourmand, et le scalaire $c est 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";

Donne :

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

0 votes

Comment cela fonctionnerait-il si vous placiez une variable à droite de b?

3 votes

@Panzercrisis c'est robuste - a,*b,c = "foo:bar:baz:last".split(":") donne a="foo" b=["bar","baz"] c="last" MODIFICATION : Il va mourir si vous ne lui donnez pas suffisamment de valeurs pour les choses définitives, c'est-à-dire le même énoncé avec "foo" étant séparé donnera ValueError: not enough values to unpack (expected at least 2, got 1)

1 votes

@magu_ Cela fait une chose différente. str.partition ne réalise qu'une seule séparation. C'est donc comme passer maxsplit=1.

21voto

Philippe Aubertin Points 860

Vous êtes toujours libre de capturer l'exception.

Par exemple:

some_string = "foo"

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

Si l'attribution de la chaîne originale entière à a et une chaîne vide à b est le comportement souhaité, je utiliserais probablement str.partition() comme le suggère eugene y. Cependant, cette solution vous donne plus de contrôle sur exactement ce qui se passe lorsqu'il n'y a pas de séparateur dans la chaîne, ce qui peut être utile dans certains cas.

3 votes

Cela ne fonctionnerait pas si la chaîne contenait plusieurs délimiteurs, par exemple, 'a:b:c:d:e'

17voto

Aaron Schif Points 402

split renverra toujours une liste. a, b = ... attendra toujours une liste de longueur deux. Vous pouvez utiliser quelque chose comme l = string.split(':'); a = l[0]; ....

Voici une astuce en une ligne : a, b = (string.split(':') + [None]*2)[:2]

4voto

Cheyn Shmuel Points 314

Que diriez-vous d'utiliser des expressions régulières :

import re 
string = 'un:deux:trois:quatre'

dans 3.X :

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

dans 2.X :

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

De cette façon, vous pouvez également utiliser des expressions régulières pour diviser (c'est-à-dire \d)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X