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

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

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

должно быть

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

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

29 ответов


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

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

где:

  • the […] игр один of разделители, перечисленные внутри,
  • the \- в регулярном выражении здесь, чтобы предотвратить специальную интерпретацию - в качестве индикатора диапазона символов (как в A-Z),
  • the + пропускает один или разделители (это может быть опущено благодаря filter(), но это излишне приведет к пустым строкам между сопоставленными сепараторами) и
  • filter(None, …) удаляет пустые строки, возможно созданные leading и trailing разделители (поскольку пустые строки имеют значение false boolean).

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

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

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


другой способ, без regex

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


во-первых, хочу согласиться с другими, что regex или str.translate(...) решения на основе наиболее эффективны. Для моего случая использования выполнение этой функции не было значительным, поэтому я хотел добавить идеи, которые я рассматривал с этими критериями.

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

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

Вариант 1-re.sub

Я был удивлен, не увидев ответа до сих пор использует re.подводная лодка.(..). Я нахожу это простым и естественным подходом к этой проблеме.

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.заменить

это еще несколько строк, но у него есть преимущество расширения без необходимости проверять, нужно ли вам избегать определенного символа в регулярном выражении.

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.замените на строку вместо этого, но я не думаю, что это можно сделать с неизменяемыми строками, и в то время как сопоставление со списком символов будет работать, запуск каждой замены Против каждого символа звучит чрезмерно. (Edit: см. следующий вариант для функционального примера.)

3 - functools.уменьшить

(В 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 возвращает последовательность пар, где pair[0] является результатом нашей функции, а pair[1] - группой. Используя 'если не k' мы отфильтровываем группы с сепараторами (потому что результат sep.__ содержит__ Истинно на сепараторах). Ну, вот и все - теперь у нас есть последовательность групп, где каждый из них-это слово (группа На самом деле является итерируемым поэтому мы используем вступить чтобы преобразовать его в строку).

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


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

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

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

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

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

параметр ПЭТ принимает разделители и возвращает разделенную строку в виде массива. Здесь два разделителя передаются с помощью оператора | (или). Вывод выглядит следующим образом:

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


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



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

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

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

обновил ответ


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

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

используя maketrans и перевести вы можете сделать это легко и аккуратно

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

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

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

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


вот мой пойти на раскол с несколькими deliminaters:

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

мне нравится replace() способ самый лучший. Следующая процедура изменяет все разделители, определенные в строке splitlist до первого разделителя в splitlist а затем разбивает текст на этот один разделитель. Он также учитывает if 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']

Если вы хотите обратимую операцию (сохранить разделители), вы можете использовать эту функцию:

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

в Python 3, Вы можете использовать метод от PY4E-Python для всех.

мы можем решить обе эти проблемы с помощью методов string lower, punctuation и translate. The translate наиболее тонких методов. Вот документация для translate:

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

заменить символы fromstr С символом в том же положении в tostr и удалить все символы в deletestr. The 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']

для получения дополнительной информации вы можете обратиться:


вы хотите модуль регулярных выражений Python findall() способ:

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

пример