Ответ 1
Случай, когда регулярные выражения оправданы:
import re
DATA = "Hey, you - what are you doing here!?"
print re.findall(r"[\w']+", DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
Я думаю, что я хочу сделать, это довольно распространенная задача, но я не нашел ссылки в Интернете. У меня есть текст с пунктуацией, и я хочу список слов.
"Hey, you - what are you doing here!?"
должно быть
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
Но 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(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 re
модуля:
>>> 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']
где:
[…]
соответствует одному из разделителей, перечисленных внутри,\-
в регулярном выражении здесь для предотвращения специальной интерпретации -
как индикатора диапазона символов (как в AZ
),+
пропускает один или несколько разделителей (это может быть опущено благодаря filter()
, но это излишне приведет к появлению пустых строк между согласованными разделителями), и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()
. Цель здесь - перевести все нежелательные символы в пробелы. Один-на-один замените. Опять же, новые данные не производятся. Итак, это быстро!
Затем мы используем старый добрый 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,[]) # 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 известно как Монада Список. Идея монады состоит в том, что однажды "в монаде" вы "остаетесь в монаде", пока что-то не выведет вас. Например, в 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
.
Преимущества такого подхода:
Во-первых, я хочу согласиться с другими, что решения на основе регулярных выражений или str.translate(...)
являются наиболее эффективными. Для моего варианта использования производительность этой функции была незначительной, поэтому я хотел добавить идеи, которые я рассмотрел с этими критериями.
Моя основная цель заключалась в том, чтобы обобщить идеи из некоторых других ответов на одно решение, которое могло бы работать для строк, содержащих больше, чем просто слова регулярных выражений (т.е. черный список явных подмножеств знаков препинания и символов белого текста).
Обратите внимание, что в любом подходе можно также использовать string.punctuation
вместо списка, определенного вручную.
Я был удивлен, увидев, что до сих пор нет ответа 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(...)
- но если производительность критическая, компиляция внешнего выражения может быть полезна - для моего варианта использования разница не была значительной, поэтому я предпочитаю простоту и читаемость.
Это еще несколько строк, но оно имеет возможность расширения, без необходимости проверять, нужно ли вам избегать определенного символа в регулярном выражении.
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 с строкой вместо этого, но я не думаю, что это можно сделать с неизменяемыми строками, а при сопоставлении с списком символов будет работать, запуская каждую замену против каждого персонажа звучит чрезмерно. (Изменить: см. Следующий вариант для функционального примера.)
(В 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']
Мне нравится 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, чтобы преобразовать ее в строку).
Это решение довольно общее, потому что оно использует функцию для разделения строки (вы можете разделить любое условие). Кроме того, он не создает промежуточные строки/списки (вы можете удалить join, и выражение станет ленивым, так как каждая группа является итератором)
Вместо использования функции re-re re.split вы можете добиться того же результата, используя метод series.str.split для pandas.
Сначала создайте серию с указанной строкой, а затем примените метод к этой серии.
thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')
Параметр pat принимает разделители и возвращает разделительную строку в виде массива. Здесь два разделителя передаются с использованием | (или оператором). Вывод выглядит следующим образом:
[Hey, you, what are you doing here!?]
Я снова познакомился с Python и нуждался в том же. Решение findall может быть лучше, но я придумал следующее:
tokens = [x.strip() for x in data.split(',')]
В 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]: '!"#$%&\'()*+,-./:;<=>[email protected][\\]^_'{|}~'
Для вашего примера:
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']
Для получения дополнительной информации вы можете обратиться:
используя maketrans и переводить, вы можете делать это легко и аккуратно
import string
specials = ',.!?:;"()<>[]#$=-/'
trans = string.maketrans(specials, ' '*len(specials))
body = body.translate(trans)
words = body.strip().split()
Другой способ добиться этого - использовать набор инструментов Natural Language Tool (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, как только вы получите свои жетоны.
Прежде всего, я не думаю, что ваше намерение состоит в том, чтобы фактически использовать пунктуацию в качестве разделителей в разделенных функциях. Ваше описание предполагает, что вы просто хотите исключить пунктуацию из результирующих строк.
Я встречаюсь с этим довольно часто, и для моего обычного решения не требуется re.
(требуется 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']
Конечно, вы всегда можете обобщить лямбда-функцию на любую указанную строку символов.
Прежде всего, всегда используйте 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']
обновленный ответ
Создайте функцию, которая принимает в качестве входных данных две строки (исходную строку для разделения и строку разделителя разделителей) и выводит список разделенных слов:
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
получил такую же проблему, как @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
Я думаю, что лучший ответ на ваш вопрос:
\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 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']
Мне нравится 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 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
Недавно мне нужно было сделать это, но я хотел функцию, которая несколько соответствовала функции стандартной библиотеки str.split
, эта функция ведет себя так же, как стандартная библиотека, когда 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])
ПРИМЕЧАНИЕ. Эта функция полезна только в том случае, если разделители состоят из одного символа (как в моем случае использования).
Мне нравится решение 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