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

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

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

должно быть

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

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

Случай, когда регулярные выражения оправданы:

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

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.'] 

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

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

Так много ответов, но я не могу найти какое-либо решение, эффективно выполняющее то, что буквально задает заголовок вопросов (разделение с несколькими разделителями – вместо этого многие ответы удаляют все, что не является словом, а другое). Итак, вот ответ на вопрос в заголовке, который основан на стандартном и эффективном модуле 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'] 

где:

  • \- в регулярном выражении здесь, чтобы предотвратить специальную интерпретацию - как индикатор диапазона символов, и где
  • filter(None, …) удаляет пустые строки, возможно созданные ведущими и конечными разделителями (поскольку пустые строки имеют ложное логическое значение).

Этот re.split() точно «разбивается на несколько разделителей», как это задано в заголовке вопроса.

Это решение также не страдает от проблем с не-ASCII-символами в словах (см. Первый комментарий к ответу ghostdog74 ).

Модуль re намного эффективнее, чем выполнение циклов Python и тестов «вручную».

Другой способ, без регулярного выражения

 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() 

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() удобства maketrans() . Цель здесь – перевести все нежелательные символы в пробелы. Один-на-один замените. Опять же, новые данные не производятся. Так быстро !

Затем мы используем старый добрый split() . split() по умолчанию будет работать со всеми пробельными символами, группируя их для разделения. Результатом будет список слов, которые вы хотите. И этот подход почти в 4 раза быстрее, чем re.findall() !

Подозрительный ответ :), но у меня была аналогичная дилемма и я не хотел использовать модуль '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'] 
 join = lambda x: sum(x,[]) # aka 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 известно как Монада Список. Идея монады состоит в том, что однажды «в монаде» вы «остаетесь в монаде», пока что-то не выведет вас. Например, в Haskell, скажем, вы сопоставляете функцию python range(n) -> [1,2,...,n] над списком. Если результатом является List, он будет добавлен в List на месте, поэтому вы получите что-то вроде map(range, [3,4,1]) -> [0,1,2,0,1,2,3,0] . Это называется map-append (или mappend, или, может быть, что-то вроде этого). Идея здесь в том, что у вас есть эта операция, которую вы применяете (разделение на токен), и всякий раз, когда вы это делаете, вы присоединяете результат к списку.

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

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

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

Во-первых, я хочу согласиться с другими, что решения на основе regex или 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.sub(...) внутри re.split(...) – но если производительность критическая, компиляция внешнего выражения может быть полезным – для моего варианта использования разница wasn ' t, поэтому я предпочитаю простоту и удобочитаемость.

Вариант 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() 

попробуй это:

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

это напечатает ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']

Используйте замену два раза:

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

результаты:

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

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

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

Мне нравится 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 .__ contains__ – метод, используемый оператором 'in'. В основном это то же самое, что

 lambda ch: ch in sep 

но здесь удобнее.

groupby получает нашу строку и функцию. Он разбивает строку в группах, используя эту функцию: всякий раз, когда изменяется значение функции – создается новая группа. Итак, sep .__ contains__ именно то, что нам нужно.

groupby возвращает последовательность пар, где пара [0] является результатом нашей функции, а пара [1] является группой. Используя 'if not k', мы отфильтровываем группы с разделителями (потому что результат sep .__ contains__ равен True в разделителях). Ну, вот и все – теперь у нас есть последовательность групп, где каждый из них является словом (группа на самом деле является итерируемой, поэтому мы используем join для преобразования ее в строку).

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

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

получил такую ​​же проблему, как @ooboo и нашел эту тему @ ghostdog74 вдохновил меня, может быть, кто-то найдет мое решение полезным

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

введите что-то в пространство и разделите с помощью одного и того же символа, если вы не хотите разбивать на пробелы.

Вот мой ход в расколе с несколькими разделителями:

 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 

Мне нравится метод 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] 
 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'] 

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

Я часто встречаюсь с этим, и мое обычное решение не требует повторения.

Единичная лямбда-функция с пониманием списка:

(требуется 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("-", " ") чтобы превратить дефисы в пробелы перед расколом.

Общая функция без учета Lambda или List

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

 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'] 

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

Прежде всего, всегда используйте re.compile () перед выполнением любой операции RegEx в цикле, потому что он работает быстрее, чем обычно.

поэтому для вашей проблемы сначала скомпилируйте шаблон и затем выполните действие над ним.

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

Вот ответ с некоторым объяснением.

 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'] 

обновленный ответ

Я думаю, что следующий ответ – лучший ответ на ваш вопрос:

\W+ возможно, подходит для этого случая, но может не подходить для других случаев.

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

Вот, я беру на себя это ….

 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'] 

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

 `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` 

Вы хотите, чтобы метод findall() модуля findall() Python:

http://www.regular-expressions.info/python.html

пример

Использовать списки для этого материала … кажется, проще

 data= "Hey, you - what are you doing here!?" tokens = [c for c in data if c not in (',', ' ', '-', '!', '?')] 

Мне легче понять (read..maintain), чем использовать regexp, просто потому, что я не так хорош в regexp … что имеет место для большинства из нас :). Также, если вы знаете, какой набор разделителей вы можете использовать, вы можете сохранить их в наборе. С очень большим набором, это может быть медленнее … но модуль 're' также медленный.