Как надежно разделить строку в Python?



в Perl я могу сделать:



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


и он будет работать независимо от того, содержит ли строка шаблон.



в Python, однако это не будет работать:



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


каков канонический способ предотвращения ошибок в таких случаях?

580   5  

5 ответов:

если вы разбиваете на две части (как в вашем примере), вы можете использовать str.partition() чтобы получить гарантированный аргумент размер распаковки 3:

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

str.partition() всегда возвращает 3-кортеж, независимо от того, найден разделитель или нет.

Другой альтернативой для Python 3 является использование расширенной распаковки, как описано в @cdarke это:

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

это присваивает первый разделенный элемент a и список остальных пунктов (если есть)b.

так как вы находитесь на Python 3, это легко. PEP 3132 представил приветствуемое упрощение синтаксиса при назначении кортежей -Расширенная итерационная распаковка. В прошлом, при назначении переменных в кортеже, количество элементов слева от назначения должно быть точно равно количеству элементов справа.

в Python 3 мы можем обозначить любую переменную слева в виде списка с помощью префикса со звездочкой *. Это захватит столько значений, сколько сможет, в то время как все еще заполняя переменные справа (поэтому он не должен быть самым правым элементом). Это позволяет избежать многих неприятных срезов, когда мы не знаем длину кортежа.

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

выдает:

a: foo b: []

редактировать следующие комментарии и обсуждения:

по сравнению с версией Perl, это значительно отличается, но это путь Python (3). По сравнению с версией Perl,re.split() было бы более похоже, однако вызов двигателя RE для разделения вокруг одного символа есть ненужные накладные расходы.

С несколькими элементами в Python:

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

выдает:

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

однако в Perl:

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

выдает:

a: hello b: world

можно видеть, что дополнительные элементы игнорируются или теряются в Perl. Это довольно легко реплицировать в Python, если требуется:

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

и a, *b = s.split(":") эквивалент в Perl будет

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

NB: мы не следует использовать $a и $b в общем на Perl, так как они имеют особое значение при использовании sort. Я использовал их здесь для согласованности с примером Python.

у Python есть дополнительный трюк в рукаве, мы можем распаковать любой элемент в кортеже слева:

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

выдает:

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

тогда как в эквиваленте Perl массив (@b) является жадным, и скаляра $c is 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";

выдает:

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

вы всегда можете поймать исключение.

например:

some_string = "foo"

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

если присвоить всю исходную строку a и пустая строка в b это желаемое поведение, я бы, вероятно, использовать str.partition() как предлагает Евгений Ю. Однако это решение дает вам больше контроля над тем, что именно происходит, когда в строке нет разделителя, что может быть полезно в некоторых случаях.

split всегда будет возвращать список. a, b = ... всегда ожидать, что длина списка будет два. Вы можете использовать что-то вроде l = string.split(':'); a = l[0]; ....

вот один лайнер: a, b = (string.split(':') + [None]*2)[:2]

как насчет использования регулярных выражений:

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

в 3.X:

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

в 2.X:

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

таким образом, вы также можете использовать регулярные выражения для разделения (т. е. \d)

Comments

    Ничего не найдено.