2010-11-03 2 views
0

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

вводимый текст выглядит Somthing так:

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 определяются в части, и это правильно работает (* \ $.). Однако выражение subs не подставляет его.

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"; 
       } 
     } 
} 

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

+3

Не используйте ddooublblle slackbashed строки для регулярных выражений, а затем скомпилируйте их все это время. Просто сделайте хэш-значения 'qr //' string напрямую. Не используйте '\\ 1' на RHS замен! И, пожалуйста, избавитесь от этих уродливых строк LTS. – tchrist

+2

Я уверен, что кто-то захочет прочитать сообщение. В то же время, пожалуйста, сделайте сами и всех, кто должен читать, чтобы закодировать услугу и искать '\ Q' в' perldoc perlreref'. –

+0

Простой комментарий. Этот код прошел несколько ревизий, пока я экспериментировал - я удалил qr, чтобы контролировать то, что было экранировано, а что нет. Вы можете доверять мне, что регулярные выражения работают, за исключением $ 1 \ 1 замен. – 0xDEADBEEF

ответ

2

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

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

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

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

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

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

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 

Конечно делает его проход$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" 
     ; 
} 

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

+0

Большое спасибо за ответ - обе версии работали хорошо, и ваш ответ очень информативен. Я пошел с вашей немного более элегантной версией «sub», но без параметров, если регулярное выражение имеет более одной подходящей переменной. Только один второстепенный момент - не подменю regex отсутствует окончательная оценка e? s/$ regex/$ hash -> {subs} ->()/e работает для меня. – 0xDEADBEEF

3

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

Подробнее см. В perlop about the s operator.

+0

Эрик, обратите внимание, что наличие RHS на подстановке будет '$ foo' одинаково с и без'/e', поэтому такая вещь всегда требует '/ ee'. – tchrist

+0

true true, так же как '' $ foo "и' $ foo', ответ обновлен –

Смежные вопросы