Почему в результатах split () возвращаются пустые строки?

120

Какой смысл '/segment/segment/'.split('/')возвращаться ['', 'segment', 'segment', '']?

Обратите внимание на пустые элементы. Если вы разделяете разделитель, который оказывается в первой позиции и в самом конце строки, какое дополнительное значение он дает, чтобы пустая строка возвращалась с каждого конца?

orokusaki
источник
1
У меня такой же вопрос и долго искал. Теперь я понимаю, что пустые результаты действительно важны. Спасибо за вопрос.
emeraldhieu
2
Решение состоит в том, strip()чтобы удалить '/segment/segment/'.strip('/').split('/')
начальные

Ответы:

178

str.splitдополняет str.join, поэтому

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

возвращает вам исходную строку.

Если бы пустых строк не было, то первая и последняя не '/'было бы послеjoin()

Джон Ла Рой
источник
11
Просто, но полностью отвечает на вопрос.
orokusaki
Я был шокирован, обнаружив, что фигурные кавычки действительно действительны в Python ... но, но ... как? Документы , кажется, не упоминают об этом.
Тим Пицкер
@ Тим, я понятия не имею, как эти цитаты попали туда: /
Джон Ла Рой,
7
Значит, вы не используете Microsoft Word в качестве Python IDE? :)
Тим Пицкер 08
1
@ aaa90210, кто сказал, что простые ответы не самые лучшие? Это был комментарий (сначала 5 лет назад) о том, что ответ был простым, но полностью отвечал на вопрос. Использование «но» в предложении не означает ничего плохого. Непростой ответ мог бы быть более полным ответом (например, включая соответствующие решения или PEP, относящиеся к отмеченной функциональности).
orokusaki
89

В более общем смысле, чтобы удалить пустые строки, возвращаемые в split()результатах, вы можете посмотреть на filterфункцию.

Пример:

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

возвращается

['segment', 'segment']
Франк Дернонкур
источник
3
Спасибо за это, я не знаю, почему этот ответ так низок, все остальное - примитивные вещи.
Wedge
6
Если желательно собрать результат в списке вместо получения объекта фильтра в качестве вывода, поместите всю структуру фильтра в него list(...).
Тим Виси
29

Здесь необходимо учитывать два основных момента:

  • Ожидать, что результат '/segment/segment/'.split('/')будет равен, ['segment', 'segment']разумно, но тогда теряется информация. Если split()сработало так, как вы хотели, если я вам это скажу a.split('/') == ['segment', 'segment'], вы не сможете мне сказать, что aбыло.
  • Какой должен быть результат 'a//b'.split()? ['a', 'b']?, или ['a', '', 'b']? Т.е. надо split()сливать соседние разделители? В противном случае будет очень сложно проанализировать данные, разделенные символом, а некоторые поля могут быть пустыми. Я довольно уверен , что есть много людей , которые делают хотят пустые значения в результате для вышеприведенного случая!

В конце концов, все сводится к двум вещам:

Последовательность: если у меня есть nразделители a, я получаю n+1значения обратно после split().

Должна быть возможность делать сложные вещи и легко делать простые: если вы хотите игнорировать пустые строки в результате split(), вы всегда можете сделать:

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

но если кто-то не хочет игнорировать пустые значения, он должен иметь возможность.

Язык должен выбрать одно определение - split()существует слишком много различных вариантов использования, чтобы удовлетворить требования каждого по умолчанию. Я считаю, что выбор Python правильный и самый логичный. (Кстати, одна из причин, по которой мне не нравится C, strtok()заключается в том, что он объединяет смежные разделители, что чрезвычайно затрудняет серьезный синтаксический анализ / токенизацию с ним.)

Есть одно исключение: a.split()без аргумента сжимает последовательные пробелы, но можно возразить, что в этом случае это правильно. Если вы не хотите такого поведения, вы всегда можете это сделать a.split(' ').

Алок Сингхал
источник
Для тех, кто задается вопросом, быстрее ли уничтожить повторяющиеся пробелы, а затем разделить, или разделить и взять только непустые строки, вот что я получаю: python3 -m timeit "import re ; re.sub(' +', ' foo bar baz ', '').split(' ')"-> 875 нс на цикл; python3 -m timeit "[token for token in ' foo bar baz '.split(' ') if token]"-> 616 нс на петлю
s3cur3
8

Имея x.split(y)всегда возвращает список 1 + x.count(y)элементов является драгоценной закономерностью - а @ gnibbler уже указал, что делает splitи joinточные обратный друг друг (как они , очевидно , должно быть), но и точно отображает семантику всех видов разделителей присоединенных записей ( такие как csvстроки файлов [[за исключением проблем с цитированием]], строки из /etc/groupUnix и т. д.), он позволяет (как упоминалось в ответе @ Roman) легко проверять (например) абсолютные и относительные пути (в путях к файлам и URL-адресах), и так далее.

Другой способ взглянуть на это состоит в том, что вы не должны бессмысленно выбрасывать информацию из окна без всякой выгоды. Что можно получить, сделав x.split(y)эквивалент x.strip(y).split(y)? Ничего, конечно - вторую форму легко использовать, когда вы имеете в виду именно это, но если бы первая форма произвольно считалась означающей вторую, у вас было бы много работы, когда вам действительно нужна первая ( что далеко не редко, как указано в предыдущем абзаце).

Но на самом деле мышление в терминах математической закономерности - это самый простой и самый общий способ научиться разрабатывать приемлемые API. Возьмем другой пример, очень важно, чтобы для любого действительного xи y x == x[:y] + x[y:]- это сразу указывает, почему следует исключить одну крайность среза . Чем проще инвариантное утверждение, которое вы можете сформулировать, тем больше вероятность того, что полученная семантика - это то, что вам нужно в реальной жизни - часть мистического факта, что математика очень полезна при работе со вселенной.

Попробуйте сформулировать инвариант для splitдиалекта, в котором начальные и конечные разделители имеют специальный регистр ... контрпример: строковые методы, такие как isspaceне максимально простые - x.isspace()эквивалентно x and all(c in string.whitespace for c in x)- это глупое начало x and, поэтому вы так часто обнаруживаете, что кодируете not x or x.isspace(), чтобы вернуться к простоте, которая должна была быть is...реализована в строковых методах (при этом пустая строка "есть" все, что вы хотите - вопреки здравому смыслу прохожего, возможно [[пустые множества, например ноль & c, всегда сбивали с толку большинство людей ;-)]], но полностью соответствовали очевидному, хорошо отточенному математическому здравому смыслу! -).

Алекс Мартелли
источник
5

Я не уверен, какой ответ вы ищете? Вы получаете три совпадения, потому что у вас есть три разделителя. Если вам не нужен этот пустой, просто используйте:

'/segment/segment/'.strip('/').split('/')
jamieb
источник
4
-1, потому что вы получаете четыре совпадения, а не три, и это также не отвечает на вопрос.
Роман
1
+1, чтобы противодействовать негативу .. Он не сказал, что вы получите три результата обратно. Он сказал «три совпадения» вместо «трех разделителей», что мне кажется логичным. Однако вы не получите «четырех совпадений» ни с чем. Тем не менее, в результате вы получите «четыре элемента». Кроме того, он не дает прямого ответа на вопрос «почему», но предоставляет простой способ получить то, что он действительно хочет ... что, я не думаю, заслуживает отрицательного голоса. Если вы собираетесь придираться к кому-то (не менее, чем против), пожалуйста, будьте осторожны! Ура! 8 ^)
kodybrown 08
@wasatchwizard Спасибо за разъяснения. Я ценю исправление и рекомендацию. К сожалению, сейчас мой голос заблокирован и не может быть изменен.
Роман
Мне нравится ваше решение - раздеться, а затем разделить, чтобы удалить пустой результат
Nam G VU
5

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

Простой пример: предположим, вы хотите проверить абсолютные и относительные имена файлов. Таким образом, вы можете сделать все это с разделением, не проверяя также, какой первый символ в вашем имени файла.

Римский
источник
1

Рассмотрим этот минимальный пример:

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

splitдолжен указать, что до и после разделителя '/', но нет других символов. Таким образом, он должен предоставить вам пустую строку, которая технически предшествует и следует за '/', потому что '' + '/' + '' == '/'.

timgeb
источник