Переменные регулярного выражения Perl и подстановка сопоставленных шаблонов - PullRequest
0 голосов
/ 03 ноября 2010

Может ли кто-нибудь объяснить подстановку текста регулярного выражения, когда регулярное выражение содержится в переменной? Я пытаюсь обработать некоторый текст, на самом деле спецификации конфигурации Clearcase, и заменить текст по ходу работы. Правила подстановки содержатся в массиве хешей, в которых есть регулярное выражение для сопоставления и текст для замены.

Вводимый текст выглядит примерно так:

element  /my_elem/releases/...  VERSION_STRING.020 -nocheckout

Большинство замен просто для удаления строк, которые содержат определенную текстовую строку, это работает отлично. В некоторых случаях я хочу заменить текст, но повторно использовать текст VERSION_STRING. Я пытался использовать $ 1 в выражении подстановки, но это не работает. $ 1 получает строку версии в совпадении, но замена $ 1 не работает при замене.

В этих случаях вывод должен выглядеть примерно так:

element  -directory  /my_elem/releases/... VERSION_STRING.020 -nocheckout
element  /my_elem/releases/.../*.[ch]  VERSION_STRING.020 -nocheckout

т. Один строчный ввод стал двумя выходными, а строка версии была повторно использована.

Код выглядит примерно так. Сначала регулярные выражения и подстановки:

my @Special_Regex = (   
                  { regex => "\\s*element\\s*\/my_elem_removed\\s*\/main\/\\d+\$",                  subs => "# Line removed" },
                  { regex => "\\s*element\\s*\/my_elem_changed\/releases\/\.\.\.\\s*\(\.\*\$\)", 
                    subs => "element  \-directory  \/my_elem\/releases\/\.\.\. \\1\nelement  \/my_elem\/releases\/\.\.\.\/\*\.\[ch\]  \\1" }

                );

Во втором регулярном выражении переменная $ 1 определена в части (. * \ $), И это работает правильно. Однако выражение sub не заменяет его.

 foreach my $line (<INFILE>)
        {
        chomp($line);
        my $test = $line;
        foreach my $hash (@Special_Regex)
        {
            my $regex = qr/$hash->{regex}/is;
            if($test =~ s/$regex/$hash->{subs}/)
                {
                print "$test\n";
                print "$line\n";
                print "$1\n";
                }
         }
}

Что мне не хватает? Заранее спасибо.

Ответы [ 2 ]

3 голосов
/ 03 ноября 2010

Строка подстановки в вашем регулярном выражении оценивается только один раз, что превращает $hash->{subs} в ее строку. Вы должны оценить это снова, чтобы интерполировать его внутренние переменные. Вы можете добавить модификатор e в конец регулярного выражения, который скажет Perl запустить подстановку через eval, который может выполнять вторую интерполяцию среди прочего. Вы можете применить несколько e флагов для оценки более одного раза (если у вас есть проблема, которая нуждается в этом). Как подсказывает tchrist , в этом случае вам нужно ee, так как первый eval будет просто расширять переменную, второй нужен для расширения переменных в расширении.

Более подробную информацию вы можете найти в perlop об операторе s .

2 голосов
/ 03 ноября 2010

Нет компиляции для выражения замены.Так что, единственное, что вы можете сделать, это exec или eval с флагом e:

if($test =~ s/$regex/eval qq["$hash->{subs}"]/e ) { #...

работал для меня после изменения \\1 на \$1 в строках замены.

s/$regex/$hash->{subs}/

заменяет только совпадающую часть на значение литерал , сохраненное в $hash->{subs} как полное замещение.Чтобы заставить подстановку работать, вы должны заставить Perl вычислять строку как строку , что означает, что вам даже нужно добавить кавычки обратно, чтобы получить искомое интерполяционное поведение(потому что они не являются частью строки.)

Но это немного неуклюже, поэтому я изменил выражения замены на подпрограммы:

my @Special_Regex 
    = ( 
        { regex => qr{\s*element\s+/my_elem_removed\s*/main/\d+$}
        , subs  => sub { '#Line removed' }
        }
    ,   { regex => qr{\s*element\s+/my_elem_changed/releases/\.\.\.\s*(.*$)}
        , subs  => sub { 
            return "element  -directory  /my_elem/releases/... $1\n"
                 . "element  /my_elem/releases/.../*.[ch]  $1"
                 ; 
          }
        }

    );

Я избавился от множества вещей, которыевам не нужно убегать в выражении подстановки.Поскольку вы хотите интерполировать значение $1 в строку замены, подпрограмма делает просто .И поскольку $1 будет отображаться до тех пор, пока не будет найдено что-то еще, это будет правильное значение, когда мы запустим этот код.

Итак, теперь замена выглядит следующим образом:

s/$regex/$hash->{subs}->()/e

Конечно, делая это pass $1 делает его немного более пуленепробиваемым, потому что вы не зависите отглобальный $1:

s/$regex/$hash->{subs}->( $1 )/e

Конечно, вы бы изменили саб так, как:

subs => sub {
    my $c1 = shift;
    return "element  -directory  /my_elem/releases/... $c1\n"
         . "element  /my_elem/releases/.../*.[ch]  $c1"
         ; 
}

Только одна последняя заметка: "\.\.\." не сделал то, что вы думаетеэто сделал.Вы только что получили в регулярном выражении '...', что соответствует любым трем символам.

...