Розділіть рядки на слова з декількома роздільниками меж слів


671

Я думаю, що я хочу зробити досить поширене завдання, але я не знайшов посилання в Інтернеті. У мене є текст з розділовими знаками, і я хочу список слів.

"Hey, you - what are you doing here!?"

має бути

['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Але Python працює str.split()лише з одним аргументом, тому у мене всі слова з пунктуацією після того, як я розділився з пробілом. Будь-які ідеї?



6
python's str.split()також працює без аргументів
Іван Виноградов

Відповіді:


468

Випадок, коли регулярні вирази виправдані:

import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

2
Дякую. Проте все ж цікаво - як я можу реалізувати алгоритм, що використовується в цьому модулі? І чому він не відображається в рядковому модулі?
ooboo

29
Регулярні вирази спочатку можуть бути приголомшливими, але дуже потужні. Регулярний вираз '\ w +' означає "слово слова (az тощо), повторене один або кілька разів". Тут є HOWTO щодо регулярних виразів Python: amk.ca/python/howto/regex
RichieHindle

324
Це не відповідь на питання. Це відповідь на інше запитання, яке, можливо, працює в цій конкретній ситуації. Це як би хтось запитав "як мені зробити поворот ліворуч", а відповідь, що голосує вгорі, був "зробіть наступні три повороту вправо". Він працює на певних перехрестях, але не дає потрібної відповіді. Як не дивно, відповідь на це в re, просто не findall. Відповідь, наведена нижче, re.split()є вищою.
Джессі Діллон

4
@JesseDhillon "прийняти всі підрядки, що складаються з послідовності символів слова", і "розділити на всі підрядки, що складаються з послідовності символів, що не містять слів", є буквально різними способами вираження однієї і тієї ж операції; Я не впевнений, чому б ви назвали будь-яку відповідь вище.
Марк Амері

4
@TMWP: Апостоф означає, що таке слово як don'tтрактується як одне слово, а не розділяється на donта t.
RichieHindle

574

re.split ()

re.split (шаблон, рядок [, maxsplit = 0])

Розділити рядок за появою шаблону. Якщо в шаблоні використовуються круглі дужки, то текст усіх груп у шаблоні також повертається як частина отриманого списку. Якщо maxsplit не є нульовим, максимум maxsplit відбувається розщеплення, а решта рядка повертається як кінцевий елемент списку. (Примітка про несумісність: у початковому випуску Python 1.5 maxsplit було проігноровано. Це було виправлено у наступних випусках.)

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']

13
Це рішення має перевагу в тому, що він легко адаптується до розбиття на підкреслення, що не знайде рішення: print re.split ("\ W + | _", "Тестування цього_тиру") "виходить: [" Тестування "," Це " , 'річ']
Еміль Стенстрем

63
Тепер , якби я тільки міг згадати різницю між \w, \W, \sі \S. Той, хто думав, що велика літера з прапором має перевернути її значення, повинна прострілювати через голову.
ArtOfWarfare

1
Загальним випадком розщеплення рядків є видалення порожніх рядкових рядків з кінцевого результату. Чи можливо це зробити за допомогою цього методу? re.split ('\ W +', 'abc') призводить до ['', 'a', 'b', 'c', ']]
Скотт Моркен

3
@ArtOfWarfare Загальним є використання shiftключа, щоб зробити щось протилежне. ctrl+zскасувати проти ctrl+shift+zдля повтору. Так shift w, або W, було б навпаки w.
Френк Вел

1
Ця відповідь має бути вгорі - це єдиний, який точно відповідає назви питання.
Кранач

381

Ще один швидкий спосіб зробити це без повторного виразу - це спочатку замінити символи, як показано нижче:

>>> 'a;bcd,ef g'.replace(';',' ').replace(',',' ').split()
['a', 'bcd', 'ef', 'g']

71
Швидкий і брудний, але ідеальний для мого випадку (мої роздільники були невеликим, відомим набором)
Енді Бейкер

7
Ідеально підходить для тих випадків, коли у вас немає доступу до бібліотеки RE, наприклад деяких невеликих мікроконтролерів. :-)
tu-Reinstate Monica-dor duh

11
Я думаю, що це також явніше, ніж РЕ, тому це щось на зразок дружнього. Іноді не потрібно загального рішення у всьому
Адам Хьюз

Дивовижно. У мене був .split () в ситуації з декількома входами, і мені потрібно було вловлювати, коли користувач, я, розділяв входи пробілом, а не комою. Я збирався відмовитись та переробити повторно, але ваше рішення .replace () вдарило цвяхом по голові. Дякую.
JayJay123

він отримає неправильну відповідь, коли ви не хочете розбиватися на пробіли і хочете розділити їх на інших символів.
Ахмед Амр

307

Стільки відповідей, але я не можу знайти жодного рішення, яке б ефективно дало назву запитань (розбиття на декілька можливих роздільників - натомість багато відповідей розділено на все, що не є словом, яке відрізняється). Отже, ось відповідь на питання в заголовку, що спирається на стандартний та ефективний reмодуль Python :

>>> import re  # Will be splitting on: , <space> - ! ? :
>>> filter(None, re.split("[, \-!?:]+", "Hey, you - what are you doing here!?"))
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

де:

  • ці […]матчі один з сепараторів , перераховані всередині,
  • \-в регулярному виразі тут , щоб запобігти спеціальну інтерпретацію в -якості індикатора діапазону символів (як в A-Z),
  • +пропускає один або кілька роздільників (вона може бути опущена завдяки filter(), але це було б зайве виробляти порожні рядки між узгодженими сепараторами), і
  • filter(None, …) видаляє порожні рядки, можливо, створені провідними та кінцевими роздільниками (оскільки порожні рядки мають помилкове булеве значення).

Це re.split()саме "розбивається з декількома роздільниками", про що вимагають у назві запитання.

Це рішення також захищене від проблем із символами, що не належать до ASCII, у словах, знайдених у деяких інших рішеннях (див. Перший коментар до відповіді ghostdog74 ).

reМодуль є набагато більш ефективним (по швидкості і стислості) , ніж робити петлю і тести Python «від руки»!


3
"Я не можу знайти жодного рішення, яке ефективно робить те, що буквально задає заголовок питань", - це друга відповідь, опублікована 5 років тому: stackoverflow.com/a/1059601/2642204 .
BartoszKP

17
Ця відповідь не розділяється на роздільники (від набору декількох роздільників): натомість вона розбивається на все, що не буквено-цифрове. З цього приводу я погоджуюся, що намір оригінального плаката, ймовірно, зберігає лише слова, замість того, щоб видаляти деякі розділові знаки.
Ерік О Лебігот

EOL: Я думаю, що ця відповідь розділена на множину деліметрів. Якщо ви додасте не алфавітно-цифрові рядки до рядка, який не вказаний, як, наприклад, підкреслення, вони не розбиваються, як очікувалося.
GravityWell

@GravityWell: Я не впевнений, що розумію: чи можна навести конкретний приклад?
Ерік О Лебігот

3
@EOL: Я щойно зрозумів, що мене збентежив ваш коментар "Ця відповідь не розбивається ..." Я подумав, що "це" стосується вашої відповіді від re.split, але тепер я розумію, що ви мали на увазі відповідь Гімеля. Я думаю, що ця відповідь (відповідь, яку я коментую) - найкраща відповідь :)
GravityWell

56

Інший спосіб, без регулярного вираження

import string
punc = string.punctuation
thestring = "Hey, you - what are you doing here!?"
s = list(thestring)
''.join([o for o in s if not o in punc]).split()

8
Це рішення насправді краще, ніж прийняте. Працює без символів ASCII, спробуйте "Hey, you - what are you doing here María!?". Прийняте рішення не працюватиме з попереднім прикладом.
Крістофер Рамірес

4
Я думаю, що тут є невелика проблема ... Ваш код додасть символи, розділені розділовими знаками, і, таким чином, їх не розділити ... Якщо я не помиляюся, ваш останній рядок повинен бути:''.join([o if not o in string.punctuation else ' ' for o in s]).split()
cedbeu

Бібліотеку регулярних виразів можна зробити так, щоб вона приймала умови Unicode для символів, якщо це необхідно. Крім того, це має ту ж проблему, що і раніше, як прийняте рішення: як і зараз, воно розпадається на апострофи. Ви можете захотіти o for o in s if (o in not string.punctuation or o == "'"), але тоді для однолінійки стає занадто складно, якщо ми також додамо патч cedbeu.
Даніель Н

Тут є ще одне питання. Навіть коли ми враховуємо зміни @cedbeu, цей код не працює, якщо рядок є чимось на зразок "First Name,Last Name,Street Address,City,State,Zip Code"і ми хочемо розділити лише на коми ,. Бажаним результатом буде: ['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']Що ми отримаємо замість цього:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
Стефан ван ден Аккер

4
Це рішення жахливо неефективне: спочатку список деконструюється на окремі символи, потім весь набір розділових символів проходить для кожного окремого символу в початковому рядку, потім символи збираються назад, а потім знову розбиваються. Весь цей "рух" теж дуже складний, порівняно з рішенням, що базується на регулярному вираженні: навіть якщо швидкість не має значення в даній програмі, немає необхідності в складному рішенні. Оскільки reмодуль є стандартним і дає розбірливість і швидкість, я не розумію, чому його слід уникати.
Ерік О Лебігот

39

Порада: Використовуйте string.translateдля найшвидших струнних операцій, якими володіє Python.

Деякі докази ...

По-перше, повільний шлях (вибачте pprzemek):

>>> import timeit
>>> S = 'Hey, you - what are you doing here!?'
>>> def my_split(s, seps):
...     res = [s]
...     for sep in seps:
...         s, res = res, []
...         for seq in s:
...             res += seq.split(sep)
...     return res
... 
>>> timeit.Timer('my_split(S, punctuation)', 'from __main__ import S,my_split; from string import punctuation').timeit()
54.65477919578552

Далі ми використовуємо re.findall()(відповідно до запропонованої відповіді). МНОГО швидше:

>>> timeit.Timer('findall(r"\w+", S)', 'from __main__ import S; from re import findall').timeit()
4.194725036621094

Нарешті, ми використовуємо translate:

>>> from string import translate,maketrans,punctuation 
>>> T = maketrans(punctuation, ' '*len(punctuation))
>>> timeit.Timer('translate(S, T).split()', 'from __main__ import S,T,translate').timeit()
1.2835021018981934

Пояснення:

string.translateреалізований в C і на відміну від багатьох функцій маніпулювання рядками в Python, string.translate не створює нової рядки. Отже, це відбувається так само швидко, як ви можете отримати заміну рядків.

Однак це трохи незручно, оскільки для виконання цієї магії потрібна таблиця перекладу. Можна зробити таблицю перекладу за допомогою maketrans()функції зручності. Завдання тут - перевести всі небажані символи в пробіли. Замінник один за одним. Знову ж таки, ніяких нових даних не надходить. Так це швидко !

Далі ми використовуємо старий добрий split(). split()за замовчуванням буде працювати з усіма символами пробілу, групуючи їх разом для розбиття. Результатом буде список слів, які ви хочете. І такий підхід майже в 4 рази швидший за re.findall()!


4
Я зробив тут тест, і якщо вам потрібно використовувати Unicode, використання patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)швидше, ніж перекладати, тому що ви повинні кодувати рядок перед застосуванням перетворення та декодувати кожен елемент у списку після розбиття, щоб повернутися до unicode.
Рафаель С. Кальсаверіні

Ви можете одноразово перекласти реалізацію та переконатися, що S не серед спліттерів за допомогою:s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
варильні панелі

Жоден не взятий. Ви порівнюєте яблука та апельсини. ;) моє рішення в python 3 все ще працює; P і має підтримку мультичарових роздільників. :) спробуйте це зробити простим способом, не виділяючи новий рядок. :) але правда, моя обмежується розбором парам командного рядка, а не книжкою, наприклад.
ппрземек

ви кажете "не створює нову рядок", це означає, що вона працює замість даного рядка? Я перевірив це зараз на python 2.7, і він не змінює орогінальну рядок і повертає нову.
Прокоп Хапала

26

У мене була подібна дилема і не хотілося використовувати модуль 're'.

def my_split(s, seps):
    res = [s]
    for sep in seps:
        s, res = res, []
        for seq in s:
            res += seq.split(sep)
    return res

print my_split('1111  2222 3333;4444,5555;6666', [' ', ';', ','])
['1111', '', '2222', '3333', '4444', '5555', '6666']

1
Мені це подобається. Просто примітка, порядок роздільників має значення. Вибачте, якщо це очевидно.
crizCraig

2
Чому б не використати reмодуль, який одночасно швидше і зрозуміліше (не те, що регулярні вирази особливо чіткі, а тому, що він коротший і прямий)?
Ерік О Лебігот

13

По-перше, я хочу погодитись з іншими, що str.translate(...)рішення, засноване на регулярних виразів, є найбільш ефективним. Для мого використання виконання цієї функції не було суттєвим, тому я хотів додати ідеї, які я вважав за цими критеріями.

Моя головна мета полягала в тому, щоб узагальнити ідеї з деяких інших відповідей в одне рішення, яке могло б працювати для рядків, що містять більше, ніж просто регекс-слова (тобто чорний список явного підмножини знаків пунктуації та білих символів слова).

Зауважте, що при будь-якому підході можна також розглянути можливість використання string.punctuationзамість визначеного вручну списку.

Варіант 1 - повтор

Я був здивований, не побачивши жодної відповіді досі використовує re.sub (...) . Я вважаю простим і природним підхід до цієї проблеми.

import re

my_str = "Hey, you - what are you doing here!?"

words = re.split(r'\s+', re.sub(r'[,\-!?]', ' ', my_str).strip())

У цьому рішенні я вклав виклик re.sub(...)всередину re.split(...)- але якщо продуктивність критична, компіляція регулярного виразу зовні може бути корисним - для мого випадку використання різниця не була суттєвою, тому я віддаю перевагу простоті та читабельності.

Варіант 2 - str.replace

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

my_str = "Hey, you - what are you doing here!?"

replacements = (',', '-', '!', '?')
for r in replacements:
    my_str = my_str.replace(r, ' ')

words = my_str.split()

Було б непогано мати можливість замістити str.replace замість рядка, але я не думаю, що це можна зробити за допомогою незмінних рядків, і хоча картування зі списком символів спрацювало б, запускаючи кожну заміну проти кожного символу звучить надмірно. (Редагувати: див. Наступний варіант для функціонального прикладу.)

Варіант 3 - functools.reduce

(У Python 2 reduceдоступний у глобальному просторі імен, не імпортуючи його з functools.)

import functools

my_str = "Hey, you - what are you doing here!?"

replacements = (',', '-', '!', '?')
my_str = functools.reduce(lambda s, sep: s.replace(sep, ' '), replacements, my_str)
words = my_str.split()

Хм, використовувати ще один метод str.translate- він не здатний до унікоду, але, швидше за все, швидший, ніж інші методи, і як такий може бути корисним у деяких випадках: replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))Також тут обов'язково мати заміни як рядок символів, а не кортеж або список.
MarSoft

@MarSoft Дякую! Я згадував про це у верхній частині відповіді, але вирішив не додавати його, оскільки існуючі відповіді вже добре обговорювали його.
Тейлор Едмістон

10
join = lambda x: sum(x,[])  # a.k.a. flatten1([[1],[2,3],[4]]) -> [1,2,3,4]
# ...alternatively...
join = lambda lists: [x for l in lists for x in l]

Потім це стає трилінійним:

fragments = [text]
for token in tokens:
    fragments = join(f.split(token) for f in fragments)

Пояснення

Це те, що в Хаскелл відоме як монада Лісту. Ідея, що стоїть за монадою, полягає в тому, що одного разу «в монаді» ти «залишаєшся в монаді», поки щось тебе не виведе. Наприклад, у Haskell, скажімо, ви позначаєте range(n) -> [1,2,...,n]функцію python у списку. Якщо результатом є Список, він буде доданий до Списку на місці, тож ви отримаєте щось на зразок map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Це відоме як додавання до карти (або mappend, або, можливо, щось подібне). Ідея тут полягає в тому, що ви застосували цю операцію, яку застосовуєте (розділяючи на маркер), і коли ви це робите, ви приєднуєте результат до списку.

Ви можете абстрагувати це у функції та мати tokens=string.punctuationза замовчуванням.

Переваги такого підходу:

  • Цей підхід (на відміну від наївних підходів, заснованих на регулярних виразках) може працювати з лексемами довільної довжини (що регулярне вираження також може робити з більш досконалим синтаксисом).
  • Вас не обмежують лише лексеми; ви можете мати довільну логіку замість кожного маркера, наприклад, один з "жетонів" може бути функцією, яка розбивається відповідно до того, як є вкладені дужки.

Охайне рішення Haskell, але IMO це можна записати чіткіше без відображення в Python.
Влад Імпала

@Goose: справа в тому, що 2-рядкову функцію map_then_appendможна використовувати для створення проблеми з 2-х вкладишем, а також багатьох інших проблем, які набагато простіше написати. Більшість інших рішень використовують reмодуль регулярного вираження , який не є python. Але я був незадоволений тим, як я змушую свою відповідь виглядати неелегантною та роздутою, коли це справді лаконічно ... Я збираюся її відредагувати ...
ninjagecko

це, як передбачається, працює в Python як написано? мій fragmentsрезультат - це лише список символів у рядку (включаючи лексеми).
Рік підтримує Моніку

@RickTeachey: він працює для мене і в python2, і в python3.
ninjagecko

хмммм. Можливо, приклад трохи неоднозначний. Я спробував код у відповідь всякі різні ways- включаючи мають fragments = ['the,string'], fragments = 'the,string'або fragments = list('the,string')жоден з них не виробляють правильний вихід.
Рік підтримує Моніку

5

спробуйте це:

import re

phrase = "Hey, you - what are you doing here!?"
matches = re.findall('\w+', phrase)
print matches

це надрукується ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']


4

Використовуйте заміну два рази:

a = '11223FROM33344INTO33222FROM3344'
a.replace('FROM', ',,,').replace('INTO', ',,,').split(',,,')

призводить до:

['11223', '33344', '33222', '3344']

4

Мені подобається повторно , але ось моє рішення без нього:

from itertools import groupby
sep = ' ,-!?'
s = "Hey, you - what are you doing here!?"
print [''.join(g) for k, g in groupby(s, sep.__contains__) if not k]

sep .__ містить__ - метод, використовуваний оператором "in". В основному це те саме, що

lambda ch: ch in sep

але тут зручніше.

groupby отримує наш рядок і функцію. Він розділяє рядок на групи, які використовують цю функцію: коли значення функції змінюється - створюється нова група. Отже, sep .__ містить__ саме те, що нам потрібно.

groupby повертає послідовність пар, де пара [0] є результатом нашої функції, а пара [1] - це група. Використовуючи "якщо ні k", ми фільтруємо групи з роздільниками (оскільки результат sep .__ містить__ є True на роздільниках). Ну, ось і все - тепер у нас є послідовність груп, де кожна є словом (група насправді є ітерабельною, тому ми використовуємо приєднання для перетворення її в рядок).

Це рішення є досить загальним, оскільки воно використовує функцію для розділення рядків (ви можете розділити їх за будь-якої необхідної умови). Крім того, він не створює проміжні рядки / списки (ви можете видалити приєднання, і вираз стане лінивим, оскільки кожна група є ітератором)


4

Замість використання функції повторного модуля re.split ви можете досягти такого ж результату, використовуючи метод pandas series.str.split.

Спочатку створіть серію з вищевказаним рядком, а потім застосуйте метод до серії.

thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')

параметр pat приймає роздільники і повертає розділений рядок як масив. Тут два роздільники передаються за допомогою | (або оператор). Вихід такий:

[Hey, you , what are you doing here!?]


1
Це не питання багатослівного, а факту імпорту цілої бібліотеки (яку я люблю, BTW) для виконання простого завдання після перетворення рядка в серію панди. Не дуже "Occam friendly".
zar3bski

3

Я знову знайомився з Python і мені було потрібно те саме. Рішення Findall може бути і кращим, але я придумав таке:

tokens = [x.strip() for x in data.split(',')]

Розумний, повинен працювати над усіма англійськими граматичними конструкціями, про які я можу придумати, крім em-dash без пробілів - наприклад, це. (
Обхідне

3

За допомогою макетранів та перекладу ви можете це зробити легко і акуратно

import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()

Чудова відповідь щодо Python> = 3,6
revliscano

3

У Python 3 ви можете використовувати метод з PY4E - Python для всіх .

Ми можемо вирішити обидві ці проблеми, використовуючи строкові методи lower, punctuationі translate. translateЄ найтоншим з методів. Ось документація для translate:

your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))

Замініть символи fromstrсимволом у тому самому положенні tostrта видаліть усі символи, які знаходяться deletestr. fromstrІ tostrможе бути порожніми рядками і deletestrпараметр може бути опущений.

Ви можете бачити "пунктуацію":

In [10]: import string

In [11]: string.punctuation
Out[11]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'  

Для вашого прикладу:

In [12]: your_str = "Hey, you - what are you doing here!?"

In [13]: line = your_str.translate(your_str.maketrans('', '', string.punctuation))

In [14]: line = line.lower()

In [15]: words = line.split()

In [16]: print(words)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Для отримання додаткової інформації можна звернутися до:


2
Методи translate () та maketrans () рядків цікаві, але цей метод не "розбивається на роздільники" (або пробіл): наприклад, "З'явилася велика печера", замість цього буде неправильно видано слово "Cavelin" очікуваного "печери" та "в" ... Таким чином, це не робить того, що запитання вимагає.
Ерік О Лебігот

Як і те, що прокоментував @EricLebigot. Метод, описаний вище, не дуже добре відповідає на запитання.
Джеремі Аніфакк

2

Ще один спосіб досягти цього - використовувати набір інструментів з природних мов ( nltk ).

import nltk
data= "Hey, you - what are you doing here!?"
word_tokens = nltk.tokenize.regexp_tokenize(data, r'\w+')
print word_tokens

Це відбитки: ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Найбільшим недоліком цього методу є те, що вам потрібно встановити пакет nltk .

Переваги полягають у тому, що ви можете зробити дуже цікаве з рештою пакету nltk, як тільки отримаєте ваші жетони.


1

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

Я стикаюсь з цим досить часто, і моє звичайне рішення не потребує повторної роботи.

Однолінійна лямбда-функція з розумінням списку:

(вимагає import string):

split_without_punc = lambda text : [word.strip(string.punctuation) for word in 
    text.split() if word.strip(string.punctuation) != '']

# Call function
split_without_punc("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']


Функція (традиційна)

Як традиційна функція, це ще лише два рядки зі списком (крім import string) списку :

def split_without_punctuation2(text):

    # Split by whitespace
    words = text.split()

    # Strip punctuation from each word
    return [word.strip(ignore) for word in words if word.strip(ignore) != '']

split_without_punctuation2("Hey, you -- what are you doing?!")
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Це також природним чином залишить скорочення та дефісні слова недоторканими. Ви завжди text.replace("-", " ")можете перетворити дефіси на пробіли до розколу.

Загальна функція без розуміння лямбда або списку

Для більш загального рішення (де ви можете вказати символи для усунення) та без розуміння списку ви отримуєте:

def split_without(text: str, ignore: str) -> list:

    # Split by whitespace
    split_string = text.split()

    # Strip any characters in the ignore string, and ignore empty strings
    words = []
    for word in split_string:
        word = word.strip(ignore)
        if word != '':
            words.append(word)

    return words

# Situation-specific call to general function
import string
final_text = split_without("Hey, you - what are you doing?!", string.punctuation)
# returns ['Hey', 'you', 'what', 'are', 'you', 'doing']

Звичайно, ви завжди можете узагальнити функцію лямбда на будь-який заданий рядок символів.


1

Перш за все, завжди використовуйте re.compile () перед виконанням будь-якої операції RegEx у циклі, оскільки вона працює швидше, ніж звичайна.

тож для вашої проблеми спочатку складіть шаблон, а потім виконайте дії над ним.

import re
DATA = "Hey, you - what are you doing here!?"
reg_tok = re.compile("[\w']+")
print reg_tok.findall(DATA)

1

Ось відповідь з деяким поясненням.

st = "Hey, you - what are you doing here!?"

# replace all the non alpha-numeric with space and then join.
new_string = ''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])
# output of new_string
'Hey  you  what are you doing here  '

# str.split() will remove all the empty string if separator is not provided
new_list = new_string.split()

# output of new_list
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

# we can join it to get a complete string without any non alpha-numeric character
' '.join(new_list)
# output
'Hey you what are you doing'

або в одному рядку ми можемо зробити так:

(''.join([x.replace(x, ' ') if not x.isalnum() else x for x in st])).split()

# output
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

оновлена ​​відповідь


1

Створіть функцію, яка приймає за вхід два рядки (початковий рядок, який слід розділити, і розділовий рядок роздільників) та виводить список розділених слів:

def split_string(source, splitlist):
    output = []  # output list of cleaned words
    atsplit = True
    for char in source:
        if char in splitlist:
            atsplit = True
        else:
            if atsplit:
                output.append(char)  # append new word after split
                atsplit = False
            else: 
                output[-1] = output[-1] + char  # continue copying characters until next split
    return output

1

Мені подобається рішення pprzemek, тому що він не передбачає, що роздільники - це одиничні символи, і він не намагається використати регулярний вираз (який би не працював добре, якби кількість роздільників була божевільною довгою).

Ось більш зрозуміла версія вищезазначеного рішення для наочності:

def split_string_on_multiple_separators(input_string, separators):
    buffer = [input_string]
    for sep in separators:
        strings = buffer
        buffer = []  # reset the buffer
        for s in strings:
            buffer = buffer + s.split(sep)

    return buffer

0

у мене така ж проблема, як @ooboo, і знайти цю тему @ ghostdog74 мене надихнуло, можливо, хтось знайде моє рішення корисним

str1='adj:sg:nom:m1.m2.m3:pos'
splitat=':.'
''.join([ s if s not in splitat else ' ' for s in str1]).split()

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


що робити, якщо мені доведеться розділити за допомогою слова?
Харша Біяні

0

Ось мій розбіг з кількома роздільниками:

def msplit( str, delims ):
  w = ''
  for z in str:
    if z not in delims:
        w += z
    else:
        if len(w) > 0 :
            yield w
        w = ''
  if len(w) > 0 :
    yield w

0

Я думаю, що найкраща відповідь, щоб відповідати вашим потребам:

\W+ можливо підходить для даного випадку, але може не підходити для інших випадків.

filter(None, re.compile('[ |,|\-|!|?]').split( "Hey, you - what are you doing here!?")

Я згоден, \wі \Wрішення не є відповіддю на (назву) питання. Зауважте, що у своїй відповіді |слід видалити (ви думаєте про це expr0|expr1замість [char0 char1…]). Крім того, немає необхідності compile()в регулярному вираженні.
Ерік О Лебігот

0

Ось мій взяти на це ....

def split_string(source,splitlist):
    splits = frozenset(splitlist)
    l = []
    s1 = ""
    for c in source:
        if c in splits:
            if s1:
                l.append(s1)
                s1 = ""
        else:
            print s1
            s1 = s1 + c
    if s1:
        l.append(s1)
    return l

>>>out = split_string("First Name,Last Name,Street Address,City,State,Zip Code",",")
>>>print out
>>>['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']

0

Мені подобається replace()найкраще. Наступна процедура змінює всі роздільники, визначені в рядку, splitlistна перший роздільник у, splitlistа потім розбиває текст на цей один роздільник. Він також враховує, якщо splitlistтрапляється порожній рядок. Він повертає список слів, в якому немає порожніх рядків.

def split_string(text, splitlist):
    for sep in splitlist:
        text = text.replace(sep, splitlist[0])
    return filter(None, text.split(splitlist[0])) if splitlist else [text]

0
def get_words(s):
    l = []
    w = ''
    for c in s.lower():
        if c in '-!?,. ':
            if w != '': 
                l.append(w)
            w = ''
        else:
            w = w + c
    if w != '': 
        l.append(w)
    return l

Ось використання:

>>> s = "Hey, you - what are you doing here!?"
>>> print get_words(s)
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']

0

Якщо ви хочете зворотну операцію (збережіть роздільники), ви можете скористатися цією функцією:

def tokenizeSentence_Reversible(sentence):
    setOfDelimiters = ['.', ' ', ',', '*', ';', '!']
    listOfTokens = [sentence]

    for delimiter in setOfDelimiters:
        newListOfTokens = []
        for ind, token in enumerate(listOfTokens):
            ll = [([delimiter, w] if ind > 0 else [w]) for ind, w in enumerate(token.split(delimiter))]
            listOfTokens = [item for sublist in ll for item in sublist] # flattens.
            listOfTokens = filter(None, listOfTokens) # Removes empty tokens: ''
            newListOfTokens.extend(listOfTokens)

        listOfTokens = newListOfTokens

    return listOfTokens

0

Нещодавно мені потрібно було це зробити, але я хотів, щоб функція, яка дещо відповідала стандартній str.splitфункції бібліотеки , ця функція поводилася так само, як і стандартна бібліотека, коли викликається з 0 або 1 аргументом.

def split_many(string, *separators):
    if len(separators) == 0:
        return string.split()
    if len(separators) > 1:
        table = {
            ord(separator): ord(separator[0])
            for separator in separators
        }
        string = string.translate(table)
    return string.split(separators[0])

ПРИМІТКА . Ця функція корисна лише тоді, коли ваші роздільники складаються з одного символу (як це було у моєму шафі).

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