Perl регулярное выражение остановить отрицательный взгляд, забирая из следующего жадного захвата
Возьмите этот простой пример в Perl v5.22.0:
my $data = "foobar\n";
$data =~ s/(?<!bar)(\s*)$/qux$1/;
print $data;
Это печатает:
foobar
qux
но я не ожидал $data
изменить. Я также попробовал несколько более ранних версий Perl 5.x с тем же результатом.
И наоборот, я ожидаю, что эта строка с тем же регулярным выражением вызовет замену, но это не так:
my $data = "foobaz\n";
$data =~ s/(?<!bar)(\s*)$/qux$1/;
print $data;
Я не понимаю, почему это происходит. В любом из них звездочка должна быть жадной. Я догадался $1
было бы \n
заставить сравнивать отрицательную группу bar
в первом примере и baz
во втором примере. Regex101, когда я использую Perl, говорит:
Квантификатор: * От нуля до неограниченного количества раз, столько раз, сколько возможно, отдача по мере необходимости.
Так что же происходит в этом случае, если это возвращает отрицательный взгляд?
Как видно из названия, реальная проблема заключается в том, что я бы хотел не дать оглядывающимся сторонам проглотить эту вторую группу. К сожалению, это не одна буква, это просто для примера, чтобы было легче понять. Также в Perl я несколько ограничен тем, что я могу сделать с негативным просмотром, например, "Переменная с переменной длиной не реализована в регулярном выражении". Если это возможно, я бы хотел получить ответ, совместимый с Perl 5.8. Спасибо
2 ответа
Я думаю ты хочешь
$data =~ s/(?<!bar)(?<!\s)(\s*)$/qux$1/;
Следующая версия будет работать с 5.8, и я думаю, что она на самом деле быстрее (так как она переходит к концу строки и возвращается, а не проверяет два взгляда в каждой позиции):
$data =~ s/
^
(
(?:
.*
(?: [^r\s]
| [^a] r
| [^b] ar
)
)?
)
( \s* )
\z
/${1}qux$2/sx;
($
может быть использован вместо \z
; это просто микрооптимизация.)
объяснение
Без m
флаг, $
эквивалентно (?:\n?\z)
Это означает, что он совпадает с символом новой строки в конце строки и в конце строки. Это означает, что есть два возможных места для $
соответствовать foobar␊
foobar␊ (There's a LF at position 6 in
01234567 case your font can't show it.)
^^
(?<!bar)
предотвращает рассмотрение первого местоположения, но допускает второе.
(?<!bar)(\s*)$
соответствует 0 символов в позиции 7, потому что(?<=bar)
соответствует 0 символов в позиции 7.(\s*)
соответствует 0 символов в позиции 7.$
соответствует 0 символов в позиции 7.
Это единственно возможное совпадение, поэтому жадность не имеет значения.
Это действительно соответствует последней позиции, прежде чем позиция \n
и после него $
, теперь посмотрите ваше регулярное выражение:
(?<!bar)(\s*)$
до позиции не bar
: соответствует
после того, как позиция $
, соответствует (\s*)$