如何使用不确定的事件进行Perl正则表达式搜索和替换?

时间:2016-02-18 07:21:50

标签: regex perl replace

在文本文件中,我有一些变量声明,比如说:

long  foo0,
      foo1,
      foo2 ;
long  foo3;
short foo4;
short foo5, foo6 ;
long  foo7, foo8 ;

我想使用Perl为所有“long”类型变量添加后缀“_r”,即。 foo0 / 1/2/3/7/8,预期输出为:

long  foo0_r,
      foo1_r,
      foo2_r ;
long  foo3_r;
short foo4;
short foo5, foo6 ;
long  foo7_r, foo8_r ;

但是,我只能提出以下代码段:

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

while (<DATA>) {
    #apply regex match and replace
    s/(long\s+)(\w+)(\s*;)/$1$2_r$3/g;
    #print current line. 
    print;
}

##inlined data filehandle for testing. 
__DATA__
long  foo0,
      foo1,
      foo2 ;
long  foo3;
short foo4;
short foo5, foo6 ;
long  foo7, foo8 ;

它只适用于foo3,但不适用于其他“长”的foos。诀窍是语法不是固定的;相反,“long”和“;”之间可能存在不确定数量的声明,由“,”分隔。任何人都可以提供一些关于代码的提示吗?

4 个答案:

答案 0 :(得分:2)

我建议:

if (m/^long\b/) {
    s/(\s\w+)/${1}_r/g;
}
更新问题的

修改:要支持多行声明,您可以先设置$/ = ';'(这样&#34;行&#34;将由;分隔比换行符...它有点像黑客,但这意味着while循环的每次迭代都会得到一个声明,而不是单个正常行)。然后,你需要在&#34; line&#34;开头的long关键字之前支持空格。 (因为它将在上一个声明的分号后面有换行符),只需编写m/^\s*long\n/而不是m/^long\n/。最后,您需要支持而不是long转换为long_r,这现在有点棘手。 (早些时候,我们依靠\s的存在来区分它,但它不再起作用了。)最后一部分的一种方法是实际删除long,然后打印它,之前进行添加_r的替换。

总体:

$/ = ';';
while (<DATA>) {
    if (s/(\s*long)\b//) {
        print $1;
        s/(\w+)/${1}_r/g;
    }
    print;
}

答案 1 :(得分:1)

编辑忘了处理逗号。现在已经解决了这个问题;

这是一种方法

while ( my $line = <DATA> ) {
    # split line into type and vars
    my ( $type, @vars ) = split(/[\s,;]+/, $line);
    # if type is long, append '_r' to vars 
    if ( lc $type eq 'long' ) {
        @vars = map { $_ . '_r' } @vars;
    }   
    # join it all back together
    print join(' ', $type, join(', ', @vars)) . ";\n";
}   

__DATA__
long  foo0, foo1, foo2 ;
long  foo3;
short foo4;
short foo5, foo6 ;

<强>更新

更新了支持多行变量的解决方案...

my $state;
while ( my $line = <DATA> ) {
    $state .= $line;
    if ( $line =~ /;/ ) {
        if ( $state =~ /long/ ) {
            $state =~ s/(\s\w+)/$1_r/g;
        }
        print $state;
        undef $state;
    }
}

__DATA__
long  foo0,
      foo1,
      foo2 ;
long  foo3;
short foo4;
short foo5, foo6 ;
long  foo7, foo8 ;

输出

long foo0_r,
     foo1_r,
     foo2_r ;
long foo3_r;
short foo4;
short foo5, foo6 ;
long foo7_r, foo8_r ;

答案 2 :(得分:1)

您可以使用(<dependency>...</dependency>)更改记录分隔符,并使用否定lookbehind匹配该记录中不$/的所有字词。

long

可生产

use strict;
use warnings;

$/=";";
while (<DATA>) {
    s/(\w+\b)(?<!\blong)/$1_r/g if /^\s*long/;
    print;
}

##inlined data filehandle for testing.
__DATA__
long  foo0,
      foo1,
      foo2 ;
long  foo3;
short foo4;
short foo5, foo6 ;
long  foo7, foo8 ;

答案 3 :(得分:0)

我会用\ s逗号和分号分隔行,测试long是第一个单词,然后迭代其余的单词并追加_r并重新构造该行。