【问题标题】:Grep within Perl substitution (RHS)Perl 替换 (RHS) 中的 Grep
【发布时间】:2015-03-03 13:40:37
【问题描述】:

我想根据文件 $key 中定义的“字符串字符串”对替换文本。

示例输入文件$input

a b c foo
d e f moo
g h i boo

预定义的“密钥”文件$key

cow moo
code foo
ghost boo
cheer woo

期望的输出

a b c code
d e f cow
g h i ghost

我的尝试

perl -pe 's/(.*?)(\woo)/$1qq{grep -oP ".*(?=\s$2)" $key}/e' $input > $output

错误返回

syntax error at -e line 1, near "$1qq{grep -oP ".*(?=\s$2)" $key}"
syntax error at -e line 1, near "s/(.*?)(\woo)/$1qq{grep -oP ".*(?=\s$2)" $key}/ee"

我们将不胜感激。

非常欢迎提出更好的方法来实现预期结果的建议,但理想情况下可以接受的答案还包括解决方案或对使用 perl 替换的评论。

【问题讨论】:

    标签: perl grep substitution


    【解决方案1】:

    从命令行使用 perl,

    perl -lane'
      BEGIN{ local @ARGV = pop; %h = reverse map split, <> }
      print join " ", @F[0..2], $h{$F[3]};
    
    ' input key
    

    输出

    a b c code
    d e f cow
    g h i ghost
    

    更新

    perl -lane'
      BEGIN{ local @ARGV = pop; %h = reverse map /(.+)\s+(\S+)$/, <> }
      print join " ", @F[0..2], $h{$F[3]};
    
    ' input key
    

    【讨论】:

    • 漂亮简洁的解决方案,感谢您的贡献。我很想知道如果关键文件行是 z y x code foo 等并且所需的输出行是 a b c z y x code 等(输入行与之前的 a b c foo 等相同),您将如何适应
    【解决方案2】:

    这里是你如何使用awk

    awk 'FNR==NR {a[$2]=$1;next} $NF=a[$NF]' key input
    a b c code
    d e f cow
    g h i ghost
    

    它将key文件读取到数组a
    然后使用数组a 的键打印input 文件以更改最后一个字段。

    如果a[$NF] 可能是0,请使用:

    awk 'FNR==NR {a[$2]=$1;next} {$NF=a[$NF];print}' key input
    

    【讨论】:

    • 如果a[$NF] 的数值计算为零,这将失败。为正确的方法 +1。
    【解决方案3】:
    $1qq{grep -oP ".*(?=\s$2)" $key}
    

    不是有效的 Perl 表达式。也许你的意思是

    $1 . qq{grep -oP ".*(?=\s$2)" $key}
    

    尽管该表达式中还有许多其他错误。 (你在应该使用qx{}的地方使用了qq{},你忘记转义\,你使用了$key而没有为其赋值,也许更多。)

    只读取一次密钥文件的可维护解决方案:

    perl -e'
       my %lookup;
       open(my $fh, "<", shift(@ARGV))
          or die $!;
    
       while (<$fh>) {
          my ($v,$k) = split;
          $lookup{$k} = $v;
       }
    
       while (<>) {
          my @f = split;
    
          next if !@f;  # Skip blank lines.
    
          if (defined($lookup{$f[3]})) {
             warn("Can'\''t find key \"$f[3]\". Copying record unchanged.\n");
             print;
             next;
          }
    
          $f[3] = $lookup{$f[3]};
          print("@f\n");
       }
    ' keyfile.txt input.txt >output.txt
    

    【讨论】:

    • $1 . qx{grep -oP ".*(?=\s$2)" $key} 在这种情况下有效吗?通过这个(你的)更正,在我的机器上我得到一个错误,如果我没记错的话,这个过程不会退出(没有新的提示,必须 ^c)。错误:Unrecognized escape \s passed through at -e line 1.Name "main::key" used only once: possible typo at -e line 1. 在连接 (.) 或字符串中使用未初始化的值 $key 在 -e 第 1 行, 第 1 行。`
    • @okapiho,我没有检查你是否犯了其他错误,但事实证明你有。您还忘记了转义\ ,并且您从未为$key 赋值。可能还有更多。
    • 出于某种原因(超出我的技能水平),对我来说,您在第 14 行的die 打印出Can't find \n 而不是Can't find $f[3]\n。否则效果很好,我很容易理解你的代码,适应我的需要,并在perldoc.perl.org/perlintro.html的帮助下学到了很多东西,谢谢:)
    • 它永远不应该打印$F[3],而是在其他文件中找不到它时的值。这意味着您的一条记录的第四个字段的值为空白,这意味着您的一条记录的字段少于四个。也许你有一个空行?
    • 糟糕,由于事情的执行顺序,错误消息可能具有误导性。修复了错误信息。
    【解决方案4】:

    就个人而言 - 我不喜欢做一个衬里,因为它们很难阅读。

    模式替换的一般技巧是这样的:

    my %replacements;
    open ( my $keyfile, "<", "key_file.txt" ) or die $!;
    while ( $keyfile ) {
         chomp;
         my ( $value, $key ) = split;
         $replacements{$key} = $value; 
    }
    
    my $regex = join ( "\b|\b", keys %replacements ); 
    $regex = qr/$regex/; 
    
    open ( my $replace_fh, "<", "input_file" ) or die $!; 
    while ( <$replace_fh> ) {
        s/\b($regex)\b/$replacements{$1}/g;
        print;
    }
    

    它将您的输入转换为替换散列,构造一个匹配其中任何单词的正则表达式,然后使用该正则表达式“匹配” - 使用$1 散列的查找键。

    【讨论】:

    • 您能否澄清一下“在您的示例中您的密钥似乎颠倒了”的意思?在我的示例中,我没有看到我认为您的意思
    • @okapiho,他的意思是关键字段通常是第一个字段(key value)。输入中的列顺序不寻常 (value key),但没有错。我已经修复了他的代码。
    • @okapiho,注意:Sobrique 的代码会替换输入所有列中的值,而不仅仅是第四列。
    猜你喜欢
    • 2018-08-21
    • 2017-07-11
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 2011-02-15
    • 2013-03-02
    相关资源
    最近更新 更多