我正在尝试使用
匹配并在多个文件中替换一些字符串local $/;
open(FILE, "<error.c");
$document=<FILE>;
close(FILE);
$found=0;
while($document=~s/([a-z_]+)\.h/$1_new\.h/gs){
$found=$found+1;
};
open(FILE, ">error.c");
print FILE "$document";
close(FILE);'
它进入无限循环,因为替换的结果再次被搜索的正则表达式匹配。但是s///g
构造不应该避免这种情况吗?
修改
我发现foreach
循环也不会完全符合我的要求(它会替换所有出现,但只打印其中一个)。原因似乎是perl替换和搜索在foreach()
和while()
结构中表现完全不同。为了有一个解决方案来替换输出所有单独替换的多个文件,我提出了以下正文:
# mandatory user inputs
my @files;
my $subs;
my $regex;
# additional user inputs
my $fileregex = '.*';
my $retval = 0;
my $opt_testonly=0;
foreach my $file (@files){
print "FILE: $file\n";
if(not($file =~ /$fileregex/)){
print "filename does not match regular expression for filenames\n";
next;
}
# read file
local $/;
if(not(open(FILE, "<$file"))){
print STDERR "ERROR: could not open file\n";
$retval = 1;
next;
};
my $string=<FILE>;
close(FILE);
my @locations_orig;
my @matches_orig;
my @results_orig;
# find matches
while ($string =~ /$regex/g) {
push @locations_orig, [ $-[0], $+[0] ];
push @matches_orig, $&;
my $result = eval("\"$subs\"");
push @results_orig, $result;
print "MATCH: ".$&." --> ".$result." @[".$-[0].",".$+[0]."]\n";
}
# reverse order
my @locations = reverse(@locations_orig);
my @matches = reverse(@matches_orig);
my @results = reverse(@results_orig);
# number of matches
my $length=$#matches+1;
my $count;
# replace matches
for($count=0;$count<$length;$count=$count+1){
substr($string, $locations[$count][0], $locations[$count][1]-$locations[$count][0]) = $results[$count];
}
# write file
if(not($opt_testonly) and $length>0){
open(FILE, ">$file"); print FILE $string; close(FILE);
}
}
exit $retval;
它首先读取文件创建匹配列表,它们的位置和每个文件中的替换文本(打印每个匹配)。其次,它将替换从字符串结尾开始的所有事件(为了不更改先前消息的位置)。最后,如果找到匹配项,它会将字符串写回文件。肯定会更优雅,但它可以做我想要的。
答案 0 :(得分:3)
$1_new
仍然会match ([a-z_]+)
。它进入无限循环,因为你在那里使用。使用s///g
构造,ONE迭代将替换字符串中的每个出现。
要计算替换次数,请使用:
$replacements = () = $document =~ s/([a-z_]+)\.h/$1_new\.h/gs;
$replacements
将包含已替换匹配的数量。
如果您基本上只想要匹配,而不是替换:
@matches = $document =~ /([a-z_]+)\.h/gs;
然后,您可以$replacement = scalar @matches
获取他们的点数。
答案 1 :(得分:1)
我会说你过度设计了这个。我过去曾这样做过:
perl -i -p -e 's/([a-z_]+)\.h/$1_new\.h/g' error.c
当替换字符串包含匹配模式时,此方法可正常工作。
答案 2 :(得分:0)
/ g选项本身就像一个循环。我想你想要这个:
while($document=~s/([a-z_]+)(?!_new)\.h/$1_new\.h/s){
$found=$found+1;
};
因为您要将匹配替换为自身以及更多,所以需要一个负面的前瞻断言。