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


671

Я думаю, что я хочу сделать, это довольно распространенная задача, но я не нашел ссылки в Интернете. У меня есть текст с пунктуацией, и я хочу список слов.

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

должно быть

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

Но Python работает str.split()только с одним аргументом, поэтому у меня есть все слова с пунктуацией после разделения на пробел. Любые идеи?



6
Питон 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: Апостоф означает, что слово like don'tобрабатывается как одно слово, а не разделяется на donи t.
RichieHindle

574

re.split ()

re.split (pattern, string [, 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
Преимущество этого решения в том, что оно легко адаптируется для разделения на подчеркивания, чего нет в решении findall: print re.split ("\ W + | _", "Testing this_thing") 'yields: [' Testing ',' this ' , 'вещь']
Эмиль Стенстрём

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
Я думаю, что это более явно, чем RE, так что это как бы дружественно к новичкам. Иногда не нужно общее решение для всего
Адам Хьюз

Потрясающие. У меня был .split () в ситуации множественного ввода, и мне нужно было перехватить, когда пользователь, я, разделил вводы пробелом, а не запятой. Я собирался сдаться и переписать с re, но ваше решение .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: Я только что понял, что был озадачен вашим комментарием «Этот ответ не разделен ...» Я думал, что «это» относится к вашему повторному ответу, но теперь я понимаю, что вы имели в виду ответ Гимела. Я думаю, что этот ответ (ответ на который я комментирую) является лучшим ответом :)
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

Pro-Tip: Используйте 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
Я сделал здесь тест, и если вам нужно использовать юникод, использование patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)быстрее, чем перевод, потому что вы должны закодировать строку перед применением преобразования и декодировать каждый элемент в списке после разделения, чтобы вернуться к юникоду.
Рафаэль С. Калсаверини

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

Ни один не взят. Вы сравниваете яблоки и апельсины. ;) мое решение в python 3 все еще работает; P и поддерживает разделители с несколькими символами. :) попробуйте сделать это простым способом без выделения новой строки. :) но, правда, мой ограничен парсингом параметров командной строки, а не книгой, например.
pprzemek

вы говорите "не производит новую строку", то есть она работает на месте для данной строки? Я протестировал его сейчас с 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

Я был удивлен, что пока не получил ответа, использует 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)

объяснение

Это то, что в Хаскеле известно как монада List. Идея, лежащая в основе монады, заключается в том, что, оказавшись «в монаде», вы «остаетесь в монаде», пока что-то не вытеснит вас. Например, в Haskell, скажем, вы отображаете range(n) -> [1,2,...,n]функцию python на List. Если результатом будет список, он будет добавлен в список на месте, так что вы получите что-то вроде map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]. Это называется map-append (или mappend, или, может быть, что-то в этом роде). Идея заключается в том, что у вас есть эта операция, которую вы применяете (разделение на токене), и всякий раз, когда вы делаете это, вы присоединяете результат к списку.

Вы можете абстрагировать это в функцию и использовать tokens=string.punctuationпо умолчанию.

Преимущества такого подхода:

  • Этот подход (в отличие от наивных подходов на основе регулярных выражений) может работать с токенами произвольной длины (что регулярное выражение также может делать с более продвинутым синтаксисом).
  • Вы не ограничены только токенами; у вас может быть произвольная логика вместо каждого токена, например, один из «токенов» может быть функцией, которая разделяется в соответствии с тем, как вложенные скобки.

Отличное решение на Haskell, но IMO это можно написать более ясно без mappend в Python.
Влад Импала

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

это должно работать в Python как написано? Мой fragmentsрезультат - просто список символов в строке (включая токены).
Рик поддерживает Монику

@RickTeachey: у меня работает как в python2, так и в python3.
ниндзягецко

хммм. Может быть, пример немного двусмысленный. Я попробовал код в ответ всякие разные 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

Мне нравится re , но вот мое решение без него:

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 для разделителей). Ну, вот и все - теперь у нас есть последовательность групп, каждая из которых является словом (группа на самом деле является итеративной, поэтому мы используем join для преобразования ее в строку).

Это решение довольно общее, потому что оно использует функцию для разделения строк (вы можете разбить любое условие). Кроме того, он не создает промежуточные строки / списки (вы можете удалить объединение, и выражение станет ленивым, поскольку каждая группа является итератором)


4

Вместо использования функции re-split модуля reim вы можете добиться того же результата, используя метод 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
Дело не в многословности, а в факте импорта всей библиотеки (которую я люблю, кстати, кстати) для выполнения простой задачи после преобразования строки в серию панды. Не очень "Оккам дружественный".
zar3bski

3

Я заново знакомлюсь с Python и мне нужно то же самое. Решение findall может быть лучше, но я придумал это:

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

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

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 for Everybody .

Мы можем решить обе эти проблемы, используя строковые методы 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 () интересны, но этому методу не удается «разделить на разделители» (или пробелами): например, «было большое углубление» вместо этого будет неправильно получено слово «cavein» ожидаемых «пещера» и «в» ... Таким образом, это не делает то, что просит вопрос.
Эрик О Лебигот

Как и то, что прокомментировал @EricLebigot. Метод выше не делает то, о чем вопрос спрашивает очень хорошо.
Джереми Анифакк

2

Другой способ добиться этого - использовать набор инструментов Natural Language Tool Kit ( 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.