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

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

Вам не нужно escape дважды, когда вы используете raw mode.

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

Также возвращаемый тип будет списком strings. Если вы хотите, чтобы возвращаемый тип как integers и floats использовал 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]

Ответ 2

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

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

Смотрите ссылку на все findall:

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

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

  • удалить избыточные группы захвата (например, (a(b)c)abc)
  • преобразовать все группы захвата в без захвата (то есть заменить ( на (?: :), если нет обратных ссылок, которые ссылаются на значения группы в шаблоне (см. ниже)
  • используйте вместо этого re.finditer ([x.group() for x in re.finditer(pattern, s)])

В вашем случае findall вернул все захваченные тексты, которые были пустыми, потому что у вас есть \\ в строковом 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)

Ответ 3

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

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

  • string: атрибут содержит строку, переданную в функцию поиска.
  • re: объект REGEX, используемый в функции поиска.
  • groups(): список строк, захваченных группами захвата внутри REGEX.
  • group(index): чтобы извлечь захваченную строку по группе, используя index > 0.
  • group(0): вернуть строку, совпадающую с REGEX.

search останавливается, когда обнаруживает, что первый механизм создал объект 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 возвращается, когда первый механизм был обнаружен, когда '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']