如何在使用Perl进行查找和替换时追加而不是替换文本?

5
我将尝试使用Perl中的正则表达式进行搜索和替换。
我要搜索的文本是:
<space>Number<space>NumberNumberNumber

我希望你能将其替换为:

<space>Number<space>NumberNumberNumberI

我有以下正则表达式,可以找到字符串:
\s[0-9]\s[0-9[0-9][0-9] 

那么我该怎么替换字符串呢?基本上我只想在末尾添加一个“I”。

我正在使用:

perl -pi -e "s/\s[0-9]\s[0-9][0-9][0-9]/I/;" testFile 

但是这样会用I完全替换掉整个内容,而不是追加到它后面。
2个回答

9

这就是后向引用的作用。只需用括号将要捕获的文本部分括起来即可。第一组括号在$1中,第二组在$2中,以此类推。

s/(\s[0-9]\s[0-9]{3})/$1I/

在Perl 5.10中,我们获得了命名捕获功能,因此您可以这样说:

s/(?<bodytext>\s[0-9]\s[0-9]{3})/$+{bodytext}I/
< p > 尖括号内的内容是名称。名称成为 %+ 变量中的键,而值则是捕获的文本。

另一种解决方案是使用零宽度正向后瞻

s/(?<=\s[0-9]\s[0-9]{3})/I/

对于 Perl 5.10 版本新增的缩写 \K

s/\s[0-9]\s[0-9]{3}\K/I/

尝试
perl -pi -e 's/(\s[0-9]\s[0-9][0-9][0-9])/$1I/' filename

如果您使用双引号,$1 在 Perl 看到它之前会被 shell 插值。 如果您遇到某些问题,认为应该可以工作,请检查 Perl 正在处理什么。 您可以使用 B::Deparse 来实现:

perl -MO=Deparse -pi -e "s/(\s[0-9]\s[0-9][0-9][0-9])/$1I/" filename

这将产生以下输出。
BEGIN { $^I = ""; }
LINE: while (defined($_ = <ARGV>)) {
    s/(\s[0-9]\s[0-9][0-9][0-9])/I/;
}
continue {
    print $_;
}
-e syntax OK

从这可以看出$1缺失了。让我们再试一次,这次使用单引号:

perl -MO=Deparse -pi -e 's/(\s[0-9]\s[0-9][0-9][0-9])/$1I/' filename
BEGIN { $^I = ""; }
LINE: while (defined($_ = <ARGV>)) {
    s/(\s[0-9]\s[0-9][0-9][0-9])/$1I/;
}
continue {
    print $_;
}
-e syntax OK

一旦转义:

perl -MO=Deparse -pi -e "s/(\s[0-9]\s[0-9][0-9][0-9])/\$1I/" filename
BEGIN { $^I = ""; }
LINE: while (defined($_ = <ARGV>)) {
    s/(\s[0-9]\s[0-9][0-9][0-9])/$1I/;
}
continue {
    print $_;
}
-e syntax OK

仍然用“我”替换整个内容。 - n00bstack
是的,这是我的完整命令:perl -pi -e "s/(\s[0-9]\s[0-9][0-9][0-9])/$1I/" testFile.phy - n00bstack
啊,你可能会遇到问题,因为你使用的是双引号而不是单引号,你需要转义 $ 或使用单引号。 - Chas. Owens
我住在距离 Sterling 大约 10 分钟的地方!应该举办聚会!:) ...可能不是在 SO 上社交的最佳场所。我会在 Twitter 上关注你。 - n00bstack
为了完整起见,您应该提供后顾解决方案:s/(?<=\s[0-9]\s[0-9][0-9][0-9])/I/ 和新的 5.10 \K 解决方案:s/\s[0-9]\s[0-9][0-9][0-9]\K/I/。 - ysth
@ysth 是的,我确实还没有内化 \K。还在写太多5.8版本的代码。 - Chas. Owens

1
2009年的答案简化版——你可以使用零宽度正回顾后发断言(?<=pattern)或者\K,在匹配后插入一些内容:
%echo "test" | perl -p -e "s/(?<=test)/ing/"
testing
%echo "test" | perl -p -e "s/test\K/ing/"
testing
%

或者,如果你想在匹配项之前插入一些内容,可以使用零宽度正向先行断言(?=pattern)

%echo "test" | perl -p -e "s/(?=test)/#/"
#test
%

参考:


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