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