如何在Perl中使用正则表达式进行反向替换

3
假设我想用"橙子"替换"苹果",用"苹果"替换"橙子",我该如何通过Perl的正则表达式实现呢?
以下是我的代码,但运行不成功:
while (<MYFILE>) {
    if (/apple/i) {
        s/$&/orange/i;
    }

    if(/orange/i) {
        s/$&/apple/i;
    }
}

如果有一种方法可以在 if 函数成功时终止它,并强制返回 while 循环,那么上述代码应该可以工作。但我相信有一个简单的正则表达式方法可以做到这一点。

2个回答

7

首先,千万不要使用$&变量:它会减慢正则表达式引擎的速度。此外,您并不需要它;这等效于您当前的循环体:

s/apple/orange/i;
s/orange/apple/i;

然而,我们需要做两个修改:

  1. 我们想要替换所有出现的单词,而不仅仅是第一个匹配的单词。我们可以使用 /g 标志来实现。
  2. 我们想要在一次替换中完成,以便替换不会被更改回来。

我们可以通过使用将匹配字符串映射到替换的哈希表来实现这一点:

my %replacement = (
  apple  => 'orange',
  orange => 'apple',
);
while (<>) {
  s/(apple|orange)/$replacement{lc $1}/ig;
  print;
}
lc是将匹配的字符串转换为小写,这是必要的,以便将Apple替换为orange。如果想保留大小写(使我们得到Orange),可以通过删除不区分大小写的匹配并将OrangeApple添加到替换散列表中来实现。

现在,如果您的散列表有大量的替换,我们不想手动编写正则表达式(只是许多静态选择)。可以从散列表创建正确的正则表达式,例如:

my $re = join '|', map quotemeta, keys %replacement;
while (<>) {
  s/($re)/$replacement{$1}/g;
}
quotemeta函数可以转义特殊字符,这样你就可以按照字面意思匹配任意字符串。

@ruggedbuteducated qr// 引用一个正则表达式。这很方便,因为与正则表达式的相同转义规则适用。如果我使用了普通字符串,那么它将是 "\\Q$_\\E" 或者更好的 quotemeta($_)。当您需要预编译或可组合的正则表达式时,qr// 非常方便(它返回一个正则表达式对象)。map 对右侧列表中的每个项目应用操作。然后它返回转换后的项。当前项在 $_ 中。 - amon
1
@amon:qr// 的作用是将字符串编译为正则表达式。但在这里你把它转回字符串后,又将其作为参数传给join函数,这样做没有任何意义。更好的写法是 my $re = join '|', map quotemeta($_), keys %replacement - Borodin
1
$& 不会减慢匹配速度,它只是将捕获的成本添加到程序中的所有模式中,即使它们不使用捕获。 - ikegami

3
my %look = (
  "apple" => "orange",
  "orange" => "apple",
);
my $rx = join "|", keys %look;

while (<MYFILE>) {
  s! ($rx) !$look{lc($1)}!xgi;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接