【问题标题】:Perl hash formattingPerl 哈希格式化
【发布时间】:2016-08-18 16:51:53
【问题描述】:

我有一个如下的日志文件

ID: COM-1234
Program: Swimming
Name: John Doe
Description: Joined on July 1st
------------------------------ID: COM-2345
Program: Swimming
Name: Brock sen
Description: Joined on July 1st
------------------------------ID: COM-9876
Program: Swimming
Name: johny boy
Description: Joined on July 1st
------------------------------ID: COM-9090
Program: Running
Name: justin kim
Description: Good Record
------------------------------

我想根据程序(游泳、跑步等)对其进行分组,并希望显示类似,

PROGRAM:  Swimming
==>ID  
    COM-1234
    COM-2345
    COM-9876

PROGRAM:  Running
==>ID   
    COM-9090

我对 Perl 很陌生,我写了下面的文章(不完整)。

#!/usr/bin/perl
use Data::Dumper;

$/ = "%%%%";
open( AFILE, "D:\\mine\\out.txt");
while (<AFILE>)
{
@temp = split(/-{20,}/, $_);
}
close (AFILE);

my %hash = @new;
print Dumper(\%hash);

我从 perl 教程中读到哈希键值对将采用具有多个值的唯一键,但不确定如何使用它。

我能够读取文件并存储到哈希中,不确定如何处理为上述格式。非常感谢任何帮助。谢谢。

【问题讨论】:

    标签: perl


    【解决方案1】:

    我总是喜欢编写这样的程序,以便它们从 STDIN 读取,因为这使它们更加灵活。

    我会这样做:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use 5.010;
    
    # Read one "chunk" of data at a time
    local $/ = '------------------------------';
    
    # A hash to store our results.
    my %program;
    
    # While there's data on STDIN...
    while (<>) {
      # Remove the end of record marker
      chomp;
      # Skip any empty records
      # (i.e. ones with no printable characters)
      next unless /\S/;
    
      # Extract the information that we want with a regex
      my ($id, $program) = /ID: (.+?)\n.*Program: (.+?)\n/s;
      # Build a hash of arrays containing our data
      push @{$program{$program}}, $id;
    }
    
    # Now we have all the data we need, so let's display it.
    
    # Keys in %program are the program names
    foreach my $p (keys %program) {
      say "PROGRAM: $p\n==>ID";
      # $program{$p} is a reference to an array of IDs
      say "\t$_" for @{$program{$p}};
      say '';
    }
    

    假设这是在一个名为 programs.pl 的程序中,并且输入数据在 programs.txt 中,那么您将像这样运行它:

    C:/> programs.pl < programs.txt
    

    【讨论】:

    • 您已经提到了\n,那么s 标志在您的模式中的用途是什么?
    • 代替非贪婪使用否定字符类,它也给出相同的结果,但步骤更少。 demo
    • 它可以在没有/s 的情况下使用此数据。但我假设 ID 和程序名称并不总是在相邻的行上。可能是矫枉过正。
    【解决方案2】:

    始终将use warnings;use strict; 放在程序的顶部。并且始终为open 使用三个参数

    open  my $fh, "<", "D:\\mine\\out.txt";
    my %hash;
    while (<$fh>){
    
        if(/ID/)
        {
            my $nxt = <$fh>;    
            s/.*?ID: //g;
            $hash{"$nxt==>ID \n"}.="   $_";
        }
    
    
    }
    
    
    print %hash;
    

    输出

    Program: Running
    ==>ID 
       COM-9090
    Program: Swimming
    ==>ID 
       COM-1234
       COM-2345
       COM-9876
    

    我在ID 之后的行中找到了您的输入文件program。所以我用 my $nxt = &lt;$fh&gt;; 现在程序被存储到了$nxt 变量中。

    【讨论】:

    • 我猜你的意思是print Dumper(\%hash) 或类似的东西。
    【解决方案3】:
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use Data::Dumper;
    
    my %hash = ();
    open my $IN, "<", "your file name here" or die "Error: $!\n";
    while (<$IN>) {
        if ($_ =~ m/^\s*-*ID:\s*COM/) {
            (my $id) = ($_ =~ m/\s*ID:\s*(.*)/);
            my $prog_name = <$IN>;
            chomp $prog_name;
            $prog_name =~ s/Program/PROGRAM/;
            $hash{$prog_name} = [] unless $hash{$prog_name};
            push @{$hash{$prog_name}}, $id;
        }
    }
    close $IN;
    print Dumper(\%hash);
    

    输出将是:

    $VAR1 = {
          'PROGRAM: Running' => [
                                  'COM-9090'
                                ],
          'PROGRAM: Swimming' => [
                                   'COM-1234',
                                   'COM-2345',
                                   'COM-9876'
                                 ]
        };
    

    让我们看看这两行:

    $hash{$prog_name} = [] unless $hash{$prog_name};
    push @{$hash{$prog_name}}, $id;
    

    如果散列未定义,则第一行初始化一个空数组引用作为值。第二行将 ID 推送到该数组的末尾(与第一行无关)。

    此外,第一行不是强制性的。如果您只写push @{$hash{$prog_name}}, $id;,Perl 就知道您的意思,并将它解释为好像您说“转到此键的值”并在它不存在时创建它。现在您说该值是一个数组,然后将$id 推送到列表中。

    【讨论】:

    • 非常感谢,效果很好。再次感谢您的详细解释。
    • @Goku,不客气。你应该阅读 Perl 的这个功能,称为Autovivification
    • 为什么要匹配两次?第一个版本肯定可以变成:if (my ($id) = m/\s*ID:\s*(.*)/) { ... }.
    • $hash{$prog_name} = () unless $hash{$prog_name}; 这是不必要的(因为自动生存)和错误的(应该是[],而不是())。
    • @DaveCross 我提到这行是不必要的,如果他是 Perl 新手,我只想尽可能清楚。对两个命令的匹配中断的原因相同。当然,您能够在一行中做到这一点是正确的。关于括号,为什么会出错?为我工作,没有错误或警告。
    猜你喜欢
    • 1970-01-01
    • 2016-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    相关资源
    最近更新 更多