регулярное выражение в Python, это может быть улучшено? - PullRequest
2 голосов
/ 02 июня 2010

У меня есть этот фрагмент кода, который находит слова, начинающиеся с @ или #,

p = re.findall(r'@\w+|#\w+', str)

Теперь меня раздражает повторение \ w +. Я уверен, что есть способ сделать что-то вроде

p = re.findall(r'(@|#)\w+', str)

Это даст тот же результат, но это не так, вместо этого он возвращает только # и @. Как можно изменить это регулярное выражение, чтобы я не повторял \w+? Этот код близко,

p = re.findall(r'((@|#)\w+)', str)

Но он возвращает [('@many', '@'), ('@this', '@'), ('#tweet', '#')] (обратите внимание на дополнительные '@', '@' и '#'.

Кроме того, если я повторяю этот re.findall код 500 000 раз, можно ли его скомпилировать и в шаблон, а затем быстрее?

1 Ответ

10 голосов
/ 02 июня 2010

Раствор

У вас есть два варианта:

  • Использовать группу без захвата: (?:@|#)\w+
  • Или, что еще лучше, класс персонажей: [@#]\w+

Ссылки


Понимание findall

Проблема, с которой вы столкнулись, связана с тем, как findall возвращает совпадения в зависимости от количества присутствующих групп захвата.

Давайте внимательнее посмотрим на этот шаблон (аннотированный, чтобы показать группы):

((@|#)\w+)
|\___/   |
|group 2 |     # Read about groups to understand
\________/     # how they're defined and numbered/named
 group 1

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

p = re.compile(r'((@|#)\w+)')
m = p.match('@tweet')
print m.group(1)
# @tweet
print m.group(2)
# @

Теперь давайте посмотрим на документацию Python для модуля re:

findall: Возвращать все неперекрывающиеся совпадения шаблона в строке в виде списка строк. Строка сканируется слева направо, и совпадения возвращаются в указанном порядке. Если в шаблоне присутствует одна или несколько групп, вернуть список групп; это будет список кортежей, если шаблон имеет более одной группы.

Это объясняет, почему вы получаете следующее:

str = 'lala @tweet boo #this &that @foo#bar'

print(re.findall(r'((@|#)\w+)', str))
# [('@tweet', '@'), ('#this', '#'), ('@foo', '@'), ('#bar', '#')]

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

В документации также объясняется, почему вы получаете следующее:

print(re.findall(r'(@|#)\w+', str))
# ['@', '#', '@', '#']

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

Напротив, шаблоны, приведенные выше в качестве решений, не имеют групп захвата, поэтому они работают в соответствии с вашими ожиданиями:

print(re.findall(r'(?:@|#)\w+', str))
# ['@tweet', '#this', '@foo', '#bar']

print(re.findall(r'[@#]\w+', str))
# ['@tweet', '#this', '@foo', '#bar']

Ссылки

Вложения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...