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*)$

Другие вопросы по тегам