Python - регулярное выражение поиска и поиска - PullRequest
14 голосов
/ 13 ноября 2011

Мне нужно найти все совпадения в строке для заданного регулярного выражения.Я использовал findall() для этого, пока не наткнулся на случай, когда он не делал то, что я ожидал.Например:

regex = re.compile('(\d+,?)+')
s = 'There are 9,000,000 bicycles in Beijing.'

print re.search(regex, s).group(0)
> 9,000,000

print re.findall(regex, s)
> ['000']

В этом случае search() возвращает то, что мне нужно (самое длинное совпадение), но findall() ведет себя по-разному, хотя документы подразумеваютоно должно быть таким же:

findall() соответствует всем вхождениям шаблона, а не только первому, как search().

  • Почему поведение отличается?

  • Как мне достичь результата search() с findall() (или с чем-то еще)?

Ответы [ 2 ]

17 голосов
/ 13 ноября 2011

Хорошо, я вижу, что происходит ... из документов:

Если в шаблоне присутствует одна или несколько групп, вернуть список групп; это будет список кортежей, если шаблон содержит более одной группы.

Как выяснилось, у вас есть группа "(\ d + ,?)" ... так что она возвращает последнее вхождение этой группы или 000.

Одним из решений является окружение всего регулярного выражения такой группой, как эта

regex = re.compile('((\d+,?)+)')

тогда он вернет [('9,000,000', '000')], который является кортежем, содержащим обе сопоставленные группы. Конечно, вы заботитесь только о первом.

Лично я бы использовал следующее регулярное выражение

regex = re.compile('((\d+,)*\d+)')

чтобы избежать совпадений типа "это плохое число 9,123"

Edit.

Вот способ избежать необходимости заключать выражение в скобки или работать с кортежами

s = "..."
regex = re.compile('(\d+,?)+')
it = re.finditer(regex, s)

for match in it:
  print match.group(0)

finditer возвращает итератор, который вы можете использовать для доступа ко всем найденным совпадениям. эти совпадающие объекты такие же, что и re.search, поэтому group (0) возвращает ожидаемый результат.

7 голосов
/ 13 ноября 2011

@ aleph_null's answer правильно объясняет причину вашей проблемы, но я думаю, что у меня есть лучшее решение. Используйте это регулярное выражение:

regex = re.compile(r'\d+(?:,\d+)*')

Несколько причин, почему это лучше:

  1. (?:...) - это группа без захвата, поэтому вы получаете только один результат для каждого матча.

  2. \d+(?:,\d+)* - это лучшее регулярное выражение, более эффективное и с меньшей вероятностью возврата ложных срабатываний.

  3. Вы всегда должны использовать необработанные строки Python для регулярных выражений, если это возможно; вы менее вероятно будете удивлены escape-последовательностями регулярных выражений (например, \b для границы слова ), интерпретируемыми как строковые буквенные escape-последовательности (например, \b для backspace ).

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