Разбиение строки на слова и знаки препинания - PullRequest
54 голосов
/ 15 декабря 2008

Я пытаюсь разбить строку на слова и знаки препинания, добавляя знаки препинания в список, созданный разделением.

Например:

>>> c = "help, me"
>>> print c.split()
['help,', 'me']

Я действительно хочу, чтобы список выглядел так:

['help', ',', 'me']

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

Я попытался сначала проанализировать строку, а затем запустить разделение:

>>> for character in c:
...     if character in ".,;!?":
...             outputCharacter = " %s" % character
...     else:
...             outputCharacter = character
...     separatedPunctuation += outputCharacter
>>> print separatedPunctuation
help , me
>>> print separatedPunctuation.split()
['help', ',', 'me']

Это дает желаемый результат, но мучительно медленно на больших файлах.

Есть ли способ сделать это более эффективно?

Ответы [ 10 ]

77 голосов
/ 15 декабря 2008

Это более или менее способ сделать это:

>>> import re
>>> re.findall(r"[\w']+|[.,!?;]", "Hello, I'm a string!")
['Hello', ',', "I'm", 'a', 'string', '!']

Хитрость в том, чтобы не думать о том, где разбить строку, а о том, что включать в токены.

Предостережения:

  • Подчеркивание (_) считается символом внутреннего слова. Замените \ w, если вы этого не хотите.
  • Это не будет работать с (одинарными) кавычками в строке.
  • Поместите любые дополнительные знаки препинания, которые вы хотите использовать, в правую половину регулярного выражения.
  • Все, что явно не упомянуто в ре, молча отбрасывается.
30 голосов
/ 19 января 2012

Вот версия с поддержкой Юникода:

re.findall(r"\w+|[^\w\s]", text, re.UNICODE)

Первая альтернатива перехватывает последовательности символов слова (как определено юникодом, поэтому «резюме» не превратится в ['r', 'sum']); вторая ловит отдельные несловарные символы, игнорируя пробелы.

Обратите внимание, что, в отличие от верхнего ответа, это рассматривает одиночную кавычку как отдельную пунктуацию (например, "Я" -> ['I', "'", 'm']). Это кажется стандартным в НЛП, поэтому я считаю, что это особенность.

5 голосов
/ 15 декабря 2008

В синтаксисе регулярных выражений в стиле perl, \b соответствует границе слова. Это должно пригодиться для разбиения на основе регулярных выражений.

edit: Я был проинформирован о том, что "пустые совпадения" не работают в функции split модуля re Python. Я оставлю это здесь как информацию для всех, кто озадачен этой «функцией».

3 голосов
/ 15 декабря 2008

Вот моя запись.

У меня есть сомнения относительно того, насколько хорошо это будет продолжаться в смысле эффективности, или если оно охватит все случаи (обратите внимание на "!!!", сгруппированные вместе; это может или не может быть хорошей вещью).

>>> import re
>>> import string
>>> s = "Helo, my name is Joe! and i live!!! in a button; factory:"
>>> l = [item for item in map(string.strip, re.split("(\W+)", s)) if len(item) > 0]
>>> l
['Helo', ',', 'my', 'name', 'is', 'Joe', '!', 'and', 'i', 'live', '!!!', 'in', 'a', 'button', ';', 'factory', ':']
>>>

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

1 голос
/ 15 декабря 2008

Вот небольшое обновление вашей реализации. Если вы пытаетесь сделать что-то более подробное, я предлагаю заглянуть в NLTK, который предложил le dorfier.

Это может быть только немного быстрее, поскольку вместо + = используется .join (), что, как известно, быстрее .

import string

d = "Hello, I'm a string!"

result = []
word = ''

for char in d:
    if char not in string.whitespace:
        if char not in string.ascii_letters + "'":
            if word:
                    result.append(word)
            result.append(char)
            word = ''
        else:
            word = ''.join([word,char])

    else:
        if word:
            result.append(word)
            word = ''
print result
['Hello', ',', "I'm", 'a', 'string', '!']
0 голосов
/ 08 ноября 2018

Если вы собираетесь работать на английском (или некоторых других распространенных языках), вы можете использовать NLTK (есть много других инструментов, например, FreeLing ).

import nltk
sentence = "help, me"
nltk.word_tokenize(sentence)
0 голосов
/ 18 апреля 2017

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

string_big = "One of Python's coolest features is the string format operator  This operator is unique to strings"
my_list =[]
x = len(string_big)
poistion_ofspace = 0
while poistion_ofspace < x:
    for i in range(poistion_ofspace,x):
        if string_big[i] == ' ':
            break
        else:
            continue
    print string_big[poistion_ofspace:(i+1)]
    my_list.append(string_big[poistion_ofspace:(i+1)])
    poistion_ofspace = i+1

print my_list
0 голосов
/ 15 апреля 2014

Я придумал способ токенизации всех слов и \W+ шаблонов с использованием \b, который не требует жесткого кодирования:

>>> import re
>>> sentence = 'Hello, world!'
>>> tokens = [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', sentence)]
['Hello', ',', 'world', '!']

Здесь .*?\S.*? - это шаблон, соответствующий всему, что не является пробелом, и $ добавляется для соответствия последнему токену в строке, если это символ пунктуации.

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

>>> print [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', '"Oh no", she said')]
['Oh', 'no', '",', 'she', 'said']

Конечно, вы можете найти и разделить такие группы с помощью:

>>> for token in [t.strip() for t in re.findall(r'\b.*?\S.*?(?:\b|$)', '"You can", she said')]:
...     print re.findall(r'(?:\w+|\W)', token)

['You']
['can']
['"', ',']
['she']
['said']
0 голосов
/ 15 декабря 2008

Я думаю, вы можете найти всю помощь, которую вы можете себе представить, в NLTK , особенно если вы используете Python. В этом уроке подробно обсуждается этот вопрос.

0 голосов
/ 15 декабря 2008

Вы пытались использовать регулярное выражение?

http://docs.python.org/library/re.html#re-syntax


Кстати. Зачем вам "," на втором? Вы будете знать, что после написания каждого текста, т.е.

[0]

""

[1]

""

Так что, если вы хотите добавить «,», вы можете просто сделать это после каждой итерации при использовании массива.

...