如何在给定的$ start- $ end范围内执行搜索和替换?

时间:2016-02-19 10:47:21

标签: regex perl replace

比方说,一个文本文件有很多$start-$end对,并且每对内都有一些文本。我希望Perl使用$pattern对查找并替换所有$start-$end s;如果$pattern位于该对之外,则不要替换它。例如:文字:

xx START xx bingo xx bingo xx END xx bingo xx START xx bingo xx END bingo

文本中可能有任何换行符(此处未显示); $pattern可能会在一对中出现多次。预期结果是:

xx START xx okyes xx okyes xx END xx bingo xx START xx okyes xx END bingo

这项工作似乎很简单,但我只是没有弄清楚Perl正则表达式。有人能帮忙吗?

4 个答案:

答案 0 :(得分:3)

查看您的来源'我建议这里的技巧是设置$/ - 记录分隔符。

如果将其设置为单个空格,则可以逐字迭代。 然后使用range operator确定您是否在分隔符内。

示例:

#!/usr/bin/env perl

use strict;
use warnings;

local $/ = ' ';

while ( <DATA> ) {
   if (  m/START/ .. /END/ ) {
       s/bingo/okyes/g;
   } 
   print;
}

__DATA__
xx START xx bingo xx bingo xx END xx bingo xx START xx bingo xx END bingo

打印:

xx START xx okyes xx okyes xx END xx bingo xx START xx okyes xx END bingo

你可以用一个正则表达式完成这个。我建议您不要,因为它以后会非常复杂和难以理解。

答案 1 :(得分:2)

我发现使用@-@+内置数组与substr一起作为左值最简单地完成了这样的事情

$-[1]包含第一次捕获开始的字符串中的偏移量,而$+[1]包含结束时的偏移量。因此$+[1]-$-[1]是捕获的部分的长度

此程序会查找所有/START(.+?)END/,并通过对该子字符串应用正则表达式替换来编辑捕获的部分 - STARTEND之间的区域

根据您使用的实际数据

,您可能需要稍微根据这一点进行操作
use strict;
use warnings 'all';
use feature 'say';

my $str = 'xx START xx bingo xx bingo xx END xx bingo xx START xx bingo xx END bingo';
my ($start, $end, $pattern, $replacement) = qw/ START END bingo okyes /;

while ( $str =~ /\b$start\b(.+?)\b$end\b/gs ) {
     substr($str, $-[1], $+[1]-$-[1]) =~ s/$pattern/$replacement/g;
}

say $str;

输出

xx START xx okyes xx okyes xx END xx bingo xx START xx okyes xx END bingo

答案 2 :(得分:1)

将START上的每一行拆分为END,并保留一个标志,告诉您是否在范围内。

#!/usr/bin/perl
use warnings;
use strict;

my $inside;
while (<>) {
    my @strings = split /(START|END)/;
    for my $string (@strings) {
        if ('START' eq $string) {
            $inside = 1;

        } elsif ('END' eq $string) {
            undef $inside;

        } elsif ($inside) {
            $string =~ s/bingo/okyes/g;

        }

        print $string;
    }
}

或者使用散列作为开关更短一些:

#!/usr/bin/perl
use warnings;
use strict;
use Syntax::Construct qw{ // };

my $inside;
while (<>) {
    my @strings = split /(START|END)/;
    for my $string (@strings) {
        $inside = { START => 1,
                    END   => 0,
                  }->{$string} // $inside;

        $string =~ s/bingo/okyes/g if $inside;
        print $string;
    }
}

答案 3 :(得分:0)

最终使用以下代码来实现我的目标:

$_ = "xx START xx bingo xx bingo xx END xx bingo xx START xx bingo xx END bingo";
print;
print "\n";
$_ =~ s/START.*?END/($s=$&) =~ s,bingo,okyes,g; $s/ge;
print;

这是一个单正则表达式解决方案,使用s///g正则表达式中的嵌入式表达式和嵌套的s///g正则表达式。

对于这篇迟到的帖子感到抱歉,但我非常感谢@Sobrique,@ Boorin和@choroba的回复,这些回复很有启发性和帮助。