Perl:我可以在文本替换正则表达式中使用的变量中使用捕获组吗?

时间:2014-07-26 21:46:36

标签: regex perl

只是为了好玩,我对Perl很新,我试图编写一个简单的文本处理工具,但我陷入了一个简单的事情。从简单的文本文件(不是来自脚本,以及可能是关键的东西)中读取的工具规则是一个简单的模式/替换对数组,用于处理文本文件(如进程)每行的每条规则)。这是应用转换的子:

my ($text, @rules) = @_;
my @lines = split(/\n/, $text);
foreach ( @rules ) {
    my $pattern = $_->{"pattern"};
    my $replace = $_->{"replace"};
    $lines = map {
        $_ =~ s/$pattern/$replace/g;
    } @lines;
}
return join("\n", @lines);

例如,如果有pattern=[aeiou] + replace=*之类的规则,则文本Foo bar会被处理为F** b*r。这就是我想要的。

但是,我无法理解为什么我无法使用捕获组来替换文本内容。我们说pattern=([fF]) + replace=<$1>会产生<$1>oo bar,但我期待<F>oo bar。我想我错过了一件非常简单的事情。我错过了什么?

更新:

经过一些实验后,我的结果是:

sub escapeSubstLiteral {
    my ($literal) = @_;
    $literal =~ s/\//\\\//g;
    $literal;
}

sub subst {
    my ($pattern, $replace, $modifiers) = @_;
    $modifiers ||= '';
    my $expression = '$text =~ s/' . escapeSubstLiteral($pattern) . '/' . escapeSubstLiteral($replace) . '/' . $modifiers;
    return sub {
        my ($text) = @_;
        eval $expression;
        $text;
    };
}

$customSubst = subst($pattern, $replace, $modifiersToken);
$foo = $customSubst->($foo);
$bar = $customSubst->($bar);

2 个答案:

答案 0 :(得分:2)

如果您的替换字符串包含捕获变量,那么您需要评估作为字符串,因此需要将其括在双引号中,并且替换需要进行双重评估。如果你第一次转义任何已经在那里的双引号,那么它将以那种方式工作,无论那里是否有任何捕获变量。

这样的事情应该适合你。顺便说一下,我不确定在进行替换之前将字符串拆分成行是多么有用,因为没有/s修饰符,它只会对非常模糊的模式产生影响。

use strict;
use warnings;
use 5.010;

my @rules = (
  {
    pattern => '[aeiou]',
    replace => '*', 
  },
  {
    pattern => '([fF])',
    replace => '<$1>',
  },
);

say replace('then text Foo bar is processed into F** b*r', @rules);


sub replace {
  my ($text, @rules) = @_;

  my @lines = split /\n/, $text;

  for my $rule (@rules) {
    my ($pattern, $replace) = @{$rule}{qw/ pattern replace /};
    $replace =~ s/"/\\"/g;
    s/$pattern/'"'.$replace.'"'/gee for @lines;
  }

  join "\n", @lines;
}

<强>输出

th*n t*xt <F>** b*r *s pr*c*ss*d *nt* <F>** b*r

答案 1 :(得分:1)

我发布了我提出的解决方案作为评论,因为我不确定是否有更好的解决方案。由于@Borodin提出了基本相同的解决方案(他自己),我想我会发布一些我写的代码以及我对它的想法。

这是我的代码:

use strict;
use warnings;

my @rules = ({pattern => '[aeiou]', replace => '*'},
             {pattern => 't', replace => 'T'},
             {pattern => '([fF])', replace => '<$1>'});

my $text = "Foo bar\nLine two";
print $text . "\n\n";
my @lines = split("\n", $text);

foreach ( @rules ) {
    my $pattern = $_->{"pattern"};
    my $replace = '"' . $_->{"replace"} . '"';
    print "Replacing $pattern with $replace\n";
    @lines = map {
        $_ =~ s/$pattern/$replace/geer;
    } @lines;
}

print "\nOutput: \n". join("\n", @lines);

输出:

Foo bar
Line two

Replacing [aeiou] with "*"
Replacing t with "T"
Replacing ([fF]) with "<$1>"

Output: 
<F>** b*r
L*n* Tw*

基本上,当您替换其中"的内容时,这会成为一个问题,例如{pattern => 'L', replace => '"l'}。然后我们得到一些错误:

Bareword found where operator expected at (eval 7) line 1, near """l"
    (Missing operator before l?)
String found where operator expected at (eval 7) line 1, at end of line
    (Missing semicolon on previous line?)
Use of uninitialized value in substitution iterator at test11.pl line 15.

当您使用\"代替{pattern => 'L', replace => '\"l'}

时,此部分即可解决

我们的输出变为:

<F>** b*r
"l*n* tw*

但是,如果您有三个斜杠{pattern => 'L', replace => '\\\"l'},则会再次中断。

它似乎只是一个脆弱的解决方案,因为你不能在你的规则中盲目地用"替换\"。我希望有一个更好的解决方案,这就是我发布评论的原因。