Чому порожні рядки повертаються з результатами split ()?


120

Який сенс '/segment/segment/'.split('/')повернення ['', 'segment', 'segment', '']?

Помітьте порожні елементи. Якщо ви розбиваєте на роздільник, який, можливо, знаходиться в першій позиції та в самому кінці рядка, яке додаткове значення дає вам повернути порожній рядок з кожного кінця?


1
У мене те саме питання і довго його шукали. Тепер я розумію, що порожні результати справді важливі. Дякуємо за запитання
смарагдовий

2
Рішення полягає у використанні strip()для зняття провідних та кінцевих символів розділення рядка перед розщепленням:'/segment/segment/'.strip('/').split('/')
pkamb

Відповіді:


178

str.splitдоповнює str.join, так

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

повертає вам початковий рядок

Якщо порожніх рядків там не було, перша і остання '/'пропала б післяjoin()


11
Просте, але повністю відповідає на питання.
orokusaki

Я був шокований, дізнавшись, що фігурні котирування дійсно дійсні в Python ... але, але ... як? Документи , схоже, не згадують про це.
Тім Піцкер

@Tim, я не маю уявлення, як потрапили ці цитати: /
Джон Ла Руй

7
Отже, ви не використовуєте Microsoft Word як свій ID Python? :)
Тім Піцкер

1
@ aaa90210 хто сказав, що прості відповіді були не найкращими? Це був коментар (по-перше, 5 років тому) про те, як відповідь була проста, але повністю відповіла на питання. Використання "але" у реченні не означає нічого поганого. Непроста відповідь могла бути більш повною відповіддю (наприклад, включаючи відповідні рішення або ПЕП, пов'язані із зазначеною функціональністю).
orokusaki

88

Більш загально, для видалення порожніх рядків, що повертаються в split()результатах, можливо, ви захочете переглянути filterфункцію.

Приклад:

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

повертає

['segment', 'segment']

3
Дякую за це, я не знаю, чому ця відповідь так далеко, все інше - рудиментарний матеріал.
Клин

6
Якщо бажано зібрати результат у список, а не отримувати об'єкт фільтра як вихід, помістіть всю структуру фільтра list(...).
Tim Visée

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/groupв Unix тощо), це дозволяє (як згадується відповідь @ Романа) на легкі перевірки (наприклад) абсолютних проти відносних шляхів (у файлових шляхах та 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('/')

4
-1 тому що ви отримуєте чотири матчі не три, а також це насправді не відповідає на питання.
Роман

1
+1, щоб протидіяти нег. Він не сказав, що ти отримаєш три результати. Він сказав "три матчі" за "три роздільники", що для мене звучить логічно. Однак ви не отримуєте "чотири матчі" нічого. Однак у вашому результаті повертаються "чотири елементи". Крім того, це не відповідає прямо на "чому", але це дає простий спосіб отримати те, що він насправді хоче ... що, на мою думку, не заслуговує уваги. Якщо ви збираєтесь кинути когось (не менш ніж), будьте обережніші! Ура! 8 ^)
kodybrown

@wasatchwizard Дякуємо за уточнення. Я ціную виправлення та рекомендацію. На жаль, зараз мій голос заблокований і його неможливо змінити.
Роман

Я люблю ваше рішення - смужку потім розділити, щоб видалити порожній результат
Nam G VU

5

Що ж, це дозволяє вам знати, що там був роздільник. Отже, побачивши 4 результати, ви знаєте, що у вас було 3 роздільники. Це дає вам можливість робити все, що завгодно, з цією інформацією, а не з тим, щоб Python скидав порожні елементи, а потім змушував вас вручну перевіряти, починаючи чи закінчувати роздільники, якщо вам потрібно це знати.

Простий приклад: скажіть, що ви хочете перевірити абсолютну та відносну назви файлів. Таким чином, ви можете зробити це все з розділенням, не маючи необхідності перевіряти, який перший символ вашого імені файлу.


1

Розглянемо цей мінімальний приклад:

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.