Почему re.findall() находит больше совпадений, чем re.sub()?
Рассмотрим следующее:
>>> import re
>>> a = "first:second"
>>> re.findall("[^:]*", a)
['first', '', 'second', '']
>>> re.sub("[^:]*", r"(\g<0>)", a)
'(first):(second)'
re.sub()
поведение имеет больше смысла, но я также понимаю поведение re.findall()
. В конце концов, вы можете сопоставить пустую строку между first
и :
, которая состоит только из символов без двоеточия (ровно нуля из них), но почему не работает re.sub()
?
Не должен ли результат последней команды быть (first)():(second)()
?
Ответы
Ответ 1
Вы используете *, который позволяет пустые совпадения:
'first' -> matched
':' -> not in the character class but, as the pattern can be empty due
to the *, an empty string is matched -->''
'second' -> matched
'$' -> can contain an empty string before,
an empty string is matched -->''
Цитата документации для re.findall()
:
Пустые совпадения включаются в результат, если они не касаются начала другого совпадения.
Причина, по которой вы не видите пустые совпадения в подвыборных результатах, объясняется в документации для re.sub()
:
Пустые совпадения для шаблона заменяются только тогда, когда они не смежны с предыдущим совпадением.
Попробуйте следующее:
re.sub('(?:Choucroute garnie)*', '#', 'ornithorynque')
И теперь это:
print re.sub('(?:nithorynque)*', '#', 'ornithorynque')
Нет последовательных #
Ответ 2
По каким-то причинам алгоритмы для обработки пустых совпадений различны.
В случае findall
он работает как (оптимизированная версия): для каждого возможного начального индекса 0 <= я <= len (a), если строка соответствует i, затем добавьте совпадение; и избегайте дублирования результатов с помощью этого правила: если соответствие длины m в i, не ищите следующее совпадение перед я + m. Причина, по которой ваш пример возвращает ['first', '', 'second', '']
, заключается в том, что пустые совпадения найдены сразу после first
и second
, но не после двоеточия --- поскольку поиск соответствия, начинающегося с этой позиции, возвращает полную строку second
.
В случае sub
разница, как вы заметили, явно игнорирует совпадения длины 0, которые происходят сразу после другого совпадения. Хотя я понимаю, почему это может помочь избежать неожиданного поведения sub
, я не уверен, почему существует эта разница (например, почему бы findall
не использовать одно и то же правило).
Ответ 3
import re
a = "first:second:three"
print re.findall("[^:]*", a)
возвращает всю подстроку, соответствующую шаблону, здесь она дает
>>>
['first', '', 'second', '', 'three', '']
sub()
предназначен для подстановки и будет заменять самые левые неперекрывающиеся вхождения шаблона вашим замещающим. ex
import re
a = "first:second:three"
print re.sub("[^:]*", r"smile", a)
дает
>>>
smile:smile:smile
Вы можете указать количество вхождений, которые будут заменены на 4-й аргумент, count: