Своеобразная работа '*' в выражении регулярного выражения
При написании шаблона регулярных выражений для замены всех непрерывных "1" и одиночных "1" как "s". Я нашел это довольно запутанным, использование "+" (используется для сопоставления 1 или более) дал ожидаемый результат, но "*" дал странный результат
>>> l='100'
>>> import re
>>> j=re.compile(r'(1)*')
>>> m=j.sub('*',l)
>>> m
'*0*0*'
В то время как использование "+" дало ожидаемый результат.
>>> l='100'
>>> j=re.compile(r'1+')
>>> m=j.sub('*',l)
>>> m
'*00'
как '*' в регулярном выражении дает это, в то время как его поведение должно соответствовать 0 или больше.
3 ответа
(1)*
означает "соответствовать 0 или более 1". Таким образом, для 100
оно соответствует 1, пустой строке между 0 и 0 и пустой строке после последней 0. Затем вы заменяете пустые строки на "*". 1+ требует как минимум одну 1 в совпадении, поэтому она не будет соответствовать границе между символами.
Для тех читателей, любопытно, да выход Python *0*0*
и не **0*0*
, Вот тестовый скрипт на python для игры. (Regex101 имеет неправильный вывод для этого, потому что он не использует реальный движок регулярных выражений Python. Онлайн-тестировщики регулярных выражений обычно используют PCRE (который предоставляется в PHP и HTTP-сервере Apache) и подделывают целевой движок регулярных выражений. Всегда проверяйте свое регулярное выражение в живом коде!)
Здесь вы можете увидеть в JavaScript вывод будет **0*0*
(оно будет соответствовать пустой строке между 1 и 0 как новое совпадение) Это яркий пример того, почему так важно 'regex flavour'. Различные двигатели используют немного разные правила. (в этом случае, если новое совпадение начинается с 0 или границы символа)
console.log("100".replace(/(1)*/g, '*'))
Остерегайтесь шаблонов, которые не могут сравниться ни с чем. Это не четко определено, поэтому поведение зависит от двигателя. Например, вы получаете другой результат в Perl.
$ perl -e'CORE::say "100" =~ s/1*/\*/rg'
**0*0*
- В позиции 0 он соответствует 1 символу.
- В позиции 1 он соответствует 0 символам.
- [Вынужден продвигаться, чтобы избежать бесконечного цикла]
- В позиции 2 он соответствует 0 символам.
- [Вынужден продвигаться, чтобы избежать бесконечного цикла]
- В позиции 3 он соответствует 0 символам.
- [Вынужден продвигаться, чтобы избежать бесконечного цикла]
- [Не соответствует: за пределами строки]
regex = r"1*"
p = re.compile(regex)
test_str = "100"
for m in p.finditer(test_str):
print(m.start(), m.group())
Выводит 4 совпадения (поэтому regex101 показывает 4 совпадения):
0 1
1
2
3
В то время как re.sub()
заменяет 3 позиции, что является причиной re.sub()
продвижение после совпадения нулевой длины ( Python doc):
sub (pattern, repl, string, count = 0, flags = 0)
Вернуть строку, полученную путем замены самого левого неперекрывающегося вхождения шаблона в строке заменой repl.
...
Пустые совпадения для шаблона заменяются только тогда, когда они не соседствуют с предыдущим совпадением, поэтому
sub('x*', '-', 'abc')
возвращается'-a-b-c-'
,
Что означает неперекрывающееся вхождение? Это означает, когда:
первый матч закончился в начале строки, где началась первая попытка матча. Механизм регулярных выражений нуждается в способе избежать застревания в бесконечном цикле, который всегда находит одинаковое совпадение нулевой длины в начале строки.
Самое простое решение, которое используется большинством механизмов регулярных выражений, состоит в том, чтобы начать следующую попытку сопоставления через один символ после окончания предыдущего сопоставления, если предыдущее совпадение было нулевой длины.
В этом случае вторая попытка сопоставления начинается с позиции между 1 и 0 в строке, отсюда и разница.