нужна помощь с разбиением строки в Python - PullRequest
1 голос
/ 03 августа 2010

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

>>> splitter = re.compile(r'((\w*)(\d*)\-\s?(\w*)(\d*)|(?x)\$?\d+(\.\d+)?(\,\d+)?|([A-Z]\.)+|(Mr)\.|(Sen)\.|(Miss)\.|.$|\w+|[^\w\s])')
>>> splitter.split("Hello! Hi, I am debating this predicament called life. Can you help me?")

Я получаю следующий вывод.Может ли кто-нибудь указать, что мне нужно исправить, пожалуйста?Я в замешательстве по поводу всей связки "Нет".Также, если есть лучший способ токенизации строки, я бы очень признателен за дополнительную помощь.

['', 'Hello', None, None, None, None, None, None, None, None, None, None, '', '!', None, None, None, None, None, None, None, None, None, None, ' ', 'Hi', None,None, None, None, None, None, None, None, None, None, '', ',', None, None, None, None, None, None, None, None, None, None, ' ', 'I', None, None, None, None, None, None, None, None, None, None, ' ', 'am', None, None, None, None, None, None,None, None, None, None, ' ', 'debating', None, None, None, None, None, None, None, None, None, None, ' ', 'this', None, None, None, None, None, None, None, None, None, None, ' ', 'predicament', None, None, None, None, None, None, None, None, None, None, ' ', 'called', None, None, None, None, None, None, None, None, None, None, ' ', 'life', None, None, None, None, None, None, None, None, None, None, '', '.', None, None, None, None, None, None, None, None, None, None, ' ', 'Can', None, None, None, None, None, None, None, None, None, None, ' ', 'you', None, None, None, None, None, None, None, None, None, None, ' ', 'help', None, None,None, None, None, None, None, None, None, None, ' ', 'me', None, None, None, None, None, None, None, None, None, None, '', '?', None, None, None, None, None, None, None, None, None, None, '']

Вывод, который мне нужен: -

['Hello', '!', 'Hi', ',', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life', '.', 'Can', 'you', 'help', 'me', '?']

Спасибо.

Ответы [ 5 ]

4 голосов
/ 03 августа 2010

Я рекомендую NLTK токенизаторы.Тогда вам не нужно беспокоиться о утомительных регулярных выражениях:

>>> import nltk
>>> nltk.word_tokenize("Hello! Hi, I am debating this predicament called life. Can you help me?")
['Hello', '!', 'Hi', ',', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life.', 'Can', 'you', 'help', 'me', '?']
4 голосов
/ 03 августа 2010

re.split быстро истощается при использовании в качестве токенизатора.Предпочтительным является findall (или match в цикле) с шаблоном альтернатив this|that|another|more

>>> s = "Hello! Hi, I am debating this predicament called life. Can you help me?"
>>> import re
>>> re.findall(r"\w+|\S", s)
['Hello', '!', 'Hi', ',', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life', '.', 'Can', 'you', 'help', 'me', '?']
>>>

Это определяет токены как один или несколько символов «слова», или как один символ, который не являетсяпробельные.Вы можете предпочесть [A-Za-z] или [A-Za-z0-9] или что-то еще вместо \w (что позволяет подчеркнуть).Вы можете даже захотеть что-то вроде r"[A-Za-z]+|[0-9]+|\S"

Если такие вещи, как Sen., Mr. и Miss (что случилось с Mrs и Ms?) Важны для вас, ваше регулярное выражение должноне перечисляйте их, он должен просто определить токен, который заканчивается на ., и у вас должен быть словарь или набор возможных сокращений.

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

Обновление: Если вам нужно / вы хотите различать типы токенов, вы можете получитьиндекс или подобное имя без (возможно, длинной) цепочки if / elif / elif /.../ else:

>>> s = "Hello! Hi, I we 0 1 987?"

>>> pattern = r"([A-Za-z]+)|([0-9]+)|(\S)"
>>> list((m.lastindex, m.group()) for m in re.finditer(pattern, s))
[(1, 'Hello'), (3, '!'), (1, 'Hi'), (3, ','), (1, 'I'), (1, 'we'), (2, '0'), (2,     '1'), (2, '987'), (3, '?')]

>>> pattern = r"(?P<word>[A-Za-z]+)|(?P<number>[0-9]+)|(?P<other>\S)"
>>> list((m.lastgroup, m.group()) for m in re.finditer(pattern, s))
[('word', 'Hello'), ('other', '!'), ('word', 'Hi'), ('other', ','), ('word', 'I'), ('word', 'we'), ('number', '0'), ('number', '1'), ('number', '987'), ('other'
, '?')]
>>>
2 голосов
/ 03 августа 2010

Может быть что-то упущено, но я полагаю, что будет работать что-то вроде следующего:

s = "Hello! Hi, I am debating this predicament called life. Can you help me?"
s.split(" ")

Предполагается, что вам нужны пробелы.Вы должны получить что-то вроде:

['Hello!', 'Hi,', 'I', 'am', 'debating', 'this', 'predicament', 'called', 'life.', 'Can', 'you', 'help', 'me?']

С этим, если вам нужен конкретный кусок, вы, вероятно, можете зациклить его, чтобы получить то, что вам нужно.

Надеюсь, это поможет ....

1 голос
/ 03 августа 2010

Причина, по которой вы получаете все эти None, заключается в том, что в вашем регулярном выражении много групп, заключенных в скобки, разделенных |. Каждый раз, когда ваше регулярное выражение находит совпадение, оно совпадает только с одной из альтернатив, заданных |. Группы в скобках в других, неиспользованных альтернативах устанавливаются на None. И re.split по определению сообщает значения всех групп, заключенных в скобки, каждый раз, когда он получает совпадение, следовательно, в вашем результате множество None.

Вы можете довольно легко отфильтровать их (например, tokens = [t for t in tokens if t] или что-то подобное), но я думаю, что split на самом деле не тот инструмент, который вы хотите использовать для токенизации. split предназначен только для удаления пробелов. Если вы действительно хотите использовать регулярные выражения для токенизации чего-либо, вот забавный пример другого метода (я даже не собираюсь пытаться распаковать того монстра, который вы используете ... используйте опцию re.VERBOSE для любви Нед ... но, надеюсь, этот игрушечный пример даст вам идею):

tokenpattern = re.compile(r"""
(?P<words>\w+) # Things with just letters and underscores
|(?P<numbers>\d+) # Things with just digits
|(?P<other>.+?) # Anything else
""", re.VERBOSE)

Бизнес (?P<something>... позволяет идентифицировать тип искомого токена по имени в приведенном ниже коде:

for match in tokenpattern.finditer("99 bottles of beer"):
  if match.group('words'):
    # This token is a word
    word = match.group('words')
    #...
  elif match.group('numbers'):
    number = int(match.group('numbers')):
  else:
    other = match.group('other'):

Обратите внимание, что это все еще т. Е. используя набор групп в скобках, разделенных |, так что произойдет то же самое, что и в вашем коде: для каждого совпадения будет определена одна группа, а для других будет установлено значение None. Этот метод проверяет это явно.

0 голосов
/ 04 августа 2010

Возможно, он не имел в виду это как таковое, но комментарий Джона Мачина "str.split - НЕ место для начала" (как часть обмена после ответа Фрэнка V ) оказался немного проблемы. Итак ...

the_string = "Hello! Hi, I am debating this predicament called life. Can you help me?"
tokens = the_string.split()
punctuation = ['!', ',', '.', '?']
output_list = []
for token in tokens:
    if token[-1] in punctuation:
        output_list.append(token[:-1])
        output_list.append(token[-1])
    else:
        output_list.append(token)
print output_list

Это, кажется, обеспечивает запрошенный вывод.

Конечно, ответ Джона более прост с точки зрения количества строк кода. Однако у меня есть пара моментов, чтобы поддержать поддержку такого рода решения.

Я не совсем согласен с высказыванием Джейми Завински: «Некоторые люди, сталкиваясь с проблемой, думают:« Я знаю, я буду использовать регулярные выражения ». Теперь у них две проблемы. (И он не из того, что я прочитал.) Я хочу сказать, что суть в том, что регулярные выражения могут быть болезненными, если вы не привыкли к ним.

Кроме того, хотя это обычно не является проблемой, производительность вышеупомянутого решения была неизменно выше, чем у решения регулярных выражений, при измерении с timeit . Вышеупомянутое решение (с удаленным оператором печати) пришло примерно через 8,9 секунды; Решение для регулярного выражения Джона пришло примерно за 11,8 секунды. Это потребовало 10 попыток из 1 миллиона итераций в четырехъядерной двухпроцессорной системе, работающей на частоте 2,4 ГГц.

...