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