Perl 搜索和替换进入无限循环 [英] Perl search and replace enters endless loop

查看:34
本文介绍了Perl 搜索和替换进入无限循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用

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 结构所避免吗?

It enters an endless loop, because the result of the substitution is matched again by the regular expression searched for. But shouldn't this be avoided by the s///g construct?

我发现 foreach 循环也不能完全满足我的要求(它将替换所有出现的事件,但只打印其中一个).原因似乎是 perl 替换和搜索在 foreach()while() 构造中的行为完全不同.为了有一个解决方案来替换多个文件,同时输出所有单独的替换,我想出了以下正文:

I found that also a foreach loop will not do exactly what I want (it will replace all occurrences, but print only one of them). The reason seems to be that the perl substitution and and search behave quite differently in the foreach() and while() constructs. To have a solution to replace in multiple files which outputs also all individual replacements, I came up with the following body:

# 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;

它首先读取文件,创建匹配列表、它们的位置和每个文件中的替换文本(打印每个匹配).其次,它将替换从字符串末尾开始的所有出现(为了不更改先前消息的位置).最后,如果找到匹配项,它会将字符串写回文件.肯定可以更优雅,但它做我想要的.

It first reads the file creates lists of the matches, their positions and the replacement text in each file (printing each match). Second it will replace all occurrences starting from the end of the string (in order not to change the position of previous messages). Finally, if matches were found, it writes the string back to the file. Can surely be more elegant, but it does what I want.

推荐答案

$1_new 仍将匹配 ([a-z_]+).它进入无限循环,因为您在那里使用.使用 s///g 构造,一次迭代将替换字符串中的每个出现.

$1_new will still match ([a-z_]+). It enters an endless loop because you use while there. With the s///g construct, ONE iteration will replace EVERY occurence in the string.

要计算替换使用:

$replacements = () = $document =~ s/([a-z_]+)\.h/$1_new\.h/gs;

$replacements 将包含替换匹配的数量.

$replacements will contain the number of replaced matches.

如果您本质上只想要匹配项,而不是替换项:

If you essentially just want the matches, not the replacements:

@matches = $document =~ /([a-z_]+)\.h/gs;

然后您可以使用 $replacement = scalar @matches 来获取它们的数量.

You can then take $replacement = scalar @matches to obtain their count.

这篇关于Perl 搜索和替换进入无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆