Вопрос-Ответ

re.findall behaves weird

re.findall странно себя ведет

Исходная строка:

# Python 3.4.3
s = r'abc123d, hello 3.1415926, this is my book'

и вот мой шаблон:

pattern = r'-?[0-9]+(\\.[0-9]*)?|-?\\.[0-9]+'

однако, re.search может дать мне правильный результат:

m = re.search(pattern, s)
print(m) # output: <_sre.SRE_Match object; span=(3, 6), match='123'>

re.findall просто создайте пустой список:

L = re.findall(pattern, s)
print(L) # output: ['', '', '']

почему не могу re.findall предоставить мне ожидаемый список:

['123', '3.1415926']
Переведено автоматически
Ответ 1

Здесь следует отметить две вещи:


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

  • r'\\.' часть в вашем шаблоне соответствует двум последовательным символам \ и любому символу, отличному от перевода строки.

См . findall ссылку:


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


Обратите внимание, что чтобы заставить re.findall возвращать только совпадающие значения, вы обычно можете


  • удалите избыточные группы захвата (например, (a(b)c) -> abc)

  • преобразуйте все фиксирующие группы в не фиксирующие (то есть замените ( на (?:), если нет обратных ссылок, которые ссылаются на значения группы в шаблоне (тогда смотрите Ниже)

  • используйте re.finditer вместо этого ([x.group() for x in re.finditer(pattern, s)])

В вашем случае, findall вернул все захваченные тексты, которые были пустыми, потому что у вас есть \\ внутри r'' строкового литерала, который пытался соответствовать литералу \.

Чтобы сопоставить числа, вам нужно использовать

-?\d*\.?\d+

Регулярное выражение соответствует:


  • -? - Необязательный знак минус

  • \d* - Необязательные цифры

  • \.? - Необязательный десятичный разделитель

  • \d+ - 1 или более цифр.

Смотрите демонстрацию

Вот демо-версия IDEONE:

import re
s = r'abc123d, hello 3.1415926, this is my book'
pattern = r'-?\d*\.?\d+'
L = re.findall(pattern, s)
print(L)
Ответ 2
s = r'abc123d, hello 3.1415926, this is my book'
print re.findall(r'-?[0-9]+(?:\.[0-9]*)?|-?\.[0-9]+',s)

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

Вывод:['123', '3.1415926']

Также возвращаемым типом будет список строк. Если вы хотите возвращаемый тип в виде целых чисел и чисел с плавающей точкой используйте map

import re,ast
s = r'abc123d, hello 3.1415926, this is my book'
print map(ast.literal_eval,re.findall(r'-?[0-9]+(?:\.[0-9]*)?|-?\.[0-9]+',s))

Вывод: [123, 3.1415926]

Ответ 3

Просто чтобы объяснить, почему вы думаете, что это search вернуло то, что вы хотите, а findall нет?

поиск возвращает SRE_Match объект, содержащий некоторую информацию, например:


  • string : атрибут содержит строку, которая была передана функции поиска.

  • re : REGEX объект, используемый в функции поиска.

  • groups() : список строк , захваченных группами захвата внутри REGEX.

  • group(index): для извлечения захваченной строки группой с помощью index > 0.

  • group(0) : возвращает строку , соответствующую REGEX.

search останавливается, когда он находит первую сборку mach SRE_Match объекта и возвращает его, проверьте этот код:

import re

s = r'abc123d'
pattern = r'-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+'
m = re.search(pattern, s)
print(m.string) # 'abc123d'
print(m.group(0)) # REGEX matched 123
print(m.groups()) # there is only one group in REGEX (\.[0-9]*) will empy string tgis why it return (None,)

s = ', hello 3.1415926, this is my book'
m2 = re.search(pattern, s) # ', hello 3.1415926, this is my book'
print(m2.string) # abc123d
print(m2.group(0)) # REGEX matched 3.1415926
print(m2.groups()) # the captured group has captured this part '.1415926'

findall ведет себя по-другому, потому что он не просто останавливается, когда находит первую строку, он продолжает извлекать до конца текста, но если REGEX содержит хотя бы одну группу захвата, то findall возвращает не совпадающую строку, а строку, захваченную группами захвата:

import re
s = r'abc123d , hello 3.1415926, this is my book'
pattern = r'-?[0-9]+(\.[0-9]*)?|-?\.[0-9]+'
m = re.findall(pattern, s)
print(m) # ['', '.1415926']

первый element возвращает, когда был найден первый mach, который '123' был захвачен только группой захвата '', но второй element был захвачен во втором совпадении, '3.1415926' группа захвата соответствовала этой части '.1415926'.

Если вы хотите сделать так, чтобы findall возвращаемая строка совпадала, вы должны сделать все группы захвата () в вашем REGEX не захватывающие группы(?:):

import re
s = r'abc123d , hello 3.1415926, this is my book'
pattern = r'-?[0-9]+(?:\.[0-9]*)?|-?\.[0-9]+'
m = re.findall(pattern, s)
print(m) # ['123', '3.1415926']
2023-07-05 03:23 python regex