2010-08-03 2 views
1

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

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

Спасибо.

+3

Как вы думаете, какой должна быть правильная выходная мощность? –

+0

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

ответ

4

Я рекомендую токенизаторы 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', '?'] 
2

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

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

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

Надеется, что это помогает ....

+0

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

+0

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

+0

Пробел является разделителем по умолчанию, поэтому вы можете просто вызвать s.split(). – GreenMatt

4

re.split быстро выдыхается при использовании в качестве tokeniser. Предпочтительно 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 вместо того, чтобы пытаться изобрести колесо.

Update: если вам нужно/хочу, чтобы различать типы маркеров, вы можете получить индекс или название, как это без (возможно длинной) цепи, если/Элиф/Элиф /.../ еще :

>>> 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' 
, '?')] 
>>> 
+0

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

1

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

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

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

Возможно, он не имел в виду его как таковые, но комментарий Джона MACHIN в «str.split это не место, чтобы начать» (как часть обмена после Frank V's answer), было немного сложнее. Итак ...

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 ГГц.

Смежные вопросы