【问题标题】:How can I collate and summarize records from a file with Perl?如何使用 Perl 整理和汇总文件中的记录?
【发布时间】:2010-10-23 21:47:34
【问题描述】:

我有一个格式如下的文本文件:

211B1 CUSTOMER|UPDATE|  
211B2 CUSTOMER|UPDATE|  
211B3 CUSTOMER|UPDATE|  
211B4 CUSTOMER|UPDATE|  
211B5 CUSTOMER|UPDATE|  
567FR CUSTOMER|DELETE|  
647GI CUSTOMER|DELETE|  

我想要一个处理文本文件并报告以下内容的脚本:

  • 为 Acct 的 211B1,211B2,211B3,211B4,211B5 找到列 CUSTOMER 的“更新”
  • “删除”列 CUSTOMER 为 Acct's 找到:5675FR,6470GI

我可以编写简单的解决方案,但这对我来说似乎有点复杂,希望得到帮助或指导。

【问题讨论】:

    标签: perl sed awk unix


    【解决方案1】:

    collat​​e.pl

    #!/usr/bin/perl
    
    use strict;
    
    my %actions;
    while (<>) {
        my ($key, $fld, $action) = /^(\w+) (.+?)\|(.+?)\|/ or die "Failed on line $.!";
        push @{$actions{$action}{$fld}}, $key;
    }
    
    foreach my $action (keys %actions) {
        foreach my $fld (keys %{$actions{$action}}) {
            print "\"$action\" for column $fld found for Acct's: " . join(",", @{$actions{$action}{$fld}}), "\n";
        }
    }
    

    这样使用:

    perl collate.pl < input.txt > output.txt
    

    【讨论】:

    • 哇,你们太棒了!这完全符合我的要求。谢谢!这是一个很棒的网站,响应速度令人难以置信......
    • 不客气,大卫。响应时间实际上相差很大,但每个人都争相解决这样的“可爱的小问题”。 :)
    【解决方案2】:

    根据您的问题,您可以这样做:

    perl -i.bak -pe'if(/^211B[1-5]/){s/CUSTOMER/UPDATE/}elsif(/^(5675FR|6470GI)/){s/CUSTOMER/DELETE/}' filename
    

    虽然我现在注意到示例中的最后两个帐号不同,并且第二列已经具有这些值...

    【讨论】:

    • 嗯?这只是复制输入,将前几行的“CUSTOMER”更改为“UPDATE”,最后几行更改为“DELETE”。 -1.
    • 从重新阅读问题开始,我现在看到了发生了什么——OP 使用了“重新格式化”这个词,您将其解释为他想要一种搜索​​和替换操作。但实际上他的措辞很糟糕——他想总结信息,而不是“重新格式化”它。 (我现在改写了。)我会留下 -1 抱歉(我知道很严厉),因为你的解决方案没有解决正确的问题。
    • 我真是个白痴。我一直在回答OP的要求。这应该是一个教训。
    • @Anonymous:可以说最初的问题模棱两可——其他 3 名响应者可以毫不费力地推断出 OP 真正想要什么。但是,如果它能让你感觉好些,那就好好哭吧。
    • 感谢您的建议。我需要方向,现在我有一个可以效仿的人。
    【解决方案3】:

    使用 awk:

    echo '211B1 CUSTOMER|UPDATE|  
    211B2 CUSTOMER|UPDATE|  
    211B3 CUSTOMER|UPDATE|  
    211B4 CUSTOMER|UPDATE|  
    211B5 CUSTOMER|UPDATE|  
    567FR CUSTOMER|DELETE|  
    647GI CUSTOMER|DELETE|' | awk -F '[ |]' '
        BEGIN {
            upd="";del=""
        } {
          if ($3 == "UPDATE") {upd = upd" "$1};
          if ($3 == "DELETE") {del = del" "$1};
        } END {
            print "Updates:"upd; print "Deletes:"del
        }'
    

    产生:

    Updates: 211B1 211B2 211B3 211B4 211B5
    Deletes: 567FR 647GI
    

    它基本上只是将每一行分成三个字段(使用-F 选项),并根据“命令”维护一个更新和删除列表。

    BEGINEND 在所有行处理之前和之后运行,因此它们是初始化和最终输出。

    我会将其放入脚本中以使其更容易。我将其保留为命令行工具,因为这就是我通常调试 awk 脚本的方式。

    【讨论】:

      【解决方案4】:
      #!/usr/bin/perl
      
      use strict;
      use warnings;
      
      my %data;
      
      while ( my $line = <DATA> ) {
          next unless $line =~ /\S/;
          my ($acct, $col, $action) = split /\s|\|/, $line;
          push @{ $data{$action}->{$col} }, $acct;
      }
      
      for my $action ( keys %data ) {
          for my $col ( keys %{ $data{$action} } ) {
              print qq{"$action" for column $col found for acct's: },
                    join q{,}, @{ $data{$action}->{$col} }, "\n";    
          }
      
      }
      __DATA__
      211B1 CUSTOMER|UPDATE|  
      211B2 CUSTOMER|UPDATE|  
      211B3 CUSTOMER|UPDATE|  
      211B4 CUSTOMER|UPDATE|  
      211B5 CUSTOMER|UPDATE|  
      567FR CUSTOMER|DELETE|  
      647GI CUSTOMER|DELETE|
      

      【讨论】:

        【解决方案5】:

        另一个 awk 版本,虽然代码值的顺序相反,并且在每一行的末尾有一个额外的“,”

        
        BEGIN { FS="[ |]" }
        
        {
                key = $3 " for column " $2
                MAP[ key ] = $1 "," MAP[ key ]
        }
        
        END {
                for ( item in MAP ) {
                        print item " found for Acct's: " MAP[ item ]
                }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-05-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-16
          • 2022-01-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多