【问题标题】:Parsing file in Perl and store the data in Hash在 Perl 中解析文件并将数据存储在 Hash 中
【发布时间】:2020-08-24 19:41:49
【问题描述】:

我正在读取输入文件并将数据存储在哈希中。稍后我想将哈希内容打印到 csv 文件。

这是脚本:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my %hash;

while(<DATA>){
    chomp;
    
    my ($e_id, $start, $end, $priority, $node);
    
    next unless /\S/;
    
    my ($key, $val) = split /\s*:\s*/;
    
    if($key =~ /eventId/)  { $e_id     = $val; }
    if($key =~ /startTime/){ $start    = $val; }
    if($key =~ /endTime/)  { $end      = $val; }
    if($key =~ /Node/)     { $node     = $val; }
    if($key =~ /Priority/) { $priority = $val; }
    
    $hash{$e_id}{'node'}     = $node;
    $hash{$e_id}{'start'}    = $start;
    $hash{$e_id}{'end'}      = $end;
    $hash{$e_id}{'priority'} = $priority;
}

print Dumper(\%hash);

__DATA__
Priority : High
Node : Node1
startTime : 2020-08-18T03:40:00
endTime : 2020-08-18T03:45:00
eventId : 150
Text : This is for Node1 text
eventPlace : Router1

Priority : Medium
Node : Node2
startTime : 2020-08-19T00:00:10
endTime : 2020-08-19T00:00:40
eventId : 170
Text : This is for Node2 text
eventPlace : Router2

但是这里hash 没有按预期打印。 Hash 的主键应该是$e_id,辅助键应该是node,start,end,priority,并且从文件中获取它们各自的eventId的值。

我想像这样打印哈希:

$VAR1 = { '150' => {
                     'end' => 2020-08-18T03:45:00,
                     'priority' => High,
                     'start' => 2020-08-18T03:45:00,
                     'node' => Node1
                   },
          '170' => {
                     'end' => 2020-08-19T00:00:40,
                     'priority' => Medium,
                     'start' => 2020-08-19T00:00:10,
                     'node' => Node2
                   }
};

我该怎么做。还请建议一种合适的方法来读取文件(我怀疑我做错了什么)。因为它会引发警告 - Use of uninitialized value $e_id in hash element at a.pl line .., &lt;DATA&gt; line ..

【问题讨论】:

    标签: perl hash file-io


    【解决方案1】:

    无需对文件中条目的名称进行硬编码。您可以在读取文件时立即使用非常简单的循环将整个条目读入散列。这是假设每条记录由一个空行分隔。

    use strict;
    use warnings;
    use Data::Dumper;
    
    $/ = "";
    my %data;
    
    while(<DATA>) {
        my $rec = { split /\n| : /, $_ };
        $data{$rec->{eventId}} = $rec;
    }
    print Dumper \%data;
    
    
    __DATA__
    Priority : High
    Node : Node1
    startTime : 2020-08-18T03:40:00
    endTime : 2020-08-18T03:45:00
    eventId : 150
    Text : This is for Node1 text
    eventPlace : Router1
    
    Priority : Medium
    Node : Node2
    startTime : 2020-08-19T00:00:10
    endTime : 2020-08-19T00:00:40
    eventId : 170
    Text : This is for Node2 text
    eventPlace : Router2
    

    这将打印:

    $VAR1 = {
              '170' => {
                         'endTime' => '2020-08-19T00:00:40',
                         'eventPlace' => 'Router2',
                         'startTime' => '2020-08-19T00:00:10',
                         'Node' => 'Node2',
                         'Priority' => 'Medium',
                         'eventId' => '170',
                         'Text' => 'This is for Node2 text'
                       },
              '150' => {
                         'endTime' => '2020-08-18T03:45:00',
                         'eventPlace' => 'Router1',
                         'startTime' => '2020-08-18T03:40:00',
                         'Node' => 'Node1',
                         'Priority' => 'High',
                         'eventId' => '150',
                         'Text' => 'This is for Node1 text'
                       }
            };
    

    【讨论】:

      【解决方案2】:

      您正在为文件的每一行重新创建这些变量:

      $e_id, $start, $end, $priority, $node
      

      如果您想在处理后面的行时访问这些值,它们不能限定为对文件的每一行重复的循环。

      此外,您可以为每一行的记录分配字段,甚至在您填充$e_id 之前。您不想为文件的每一行分配每个字段,您需要等到您读取了整条记录后再分配给$hash{$e_id}

      我的解决方案:

      my %field_map = (
         'startTime' => 'start',
         'endTime'   => 'end',
         'Node'      => 'node',
         'Priority'  => 'priority',
      );
      
      my %recs;
      my $id;
      my $rec = { };
      while (1) {
          $_ = <DATA>;
      
          # If end of file or end of record.
          if (!defined($_) || $_ =~ /^$/) {
              $recs{$id} = $rec if defined($id);
      
              # If end of file.
              last if !defined($_);
      
              # Start a new record.
              $id = undef;
              $rec = { };
              next;
          }
      
          chomp;
          my ($key, $val) = split(/\s*:\s*/, $_, 2);
      
          if ( $key eq 'eventId' ) {
             $id = $val;
          }
          elsif ( $field_map{$key} ) {
             $rec->{ $field_map{$key} } = $val;
          }
      }
      

      【讨论】:

      • 感谢@ikegami。但是当我打印 $rec 的 Dumper 时,它只给了我最后一条记录。
      • 所以?谁在乎 $rec 最后包含什么。 (它实际上是对最后一个空散列的引用,而不是最后一条记录。)你的问题是关于建立一个记录的散列。您应该查看哈希值。
      • 对不起。错字。我的意思是%recs 哈希。因为它以eventId为键,而不是eventId的值。
      • 你应该已经注意到key也错了,这就是为什么只有一条记录。相同的键 --eventId-- 被用于所有记录,而不是正确的值。固定。
      【解决方案3】:

      如果你想在读取不同的行时使用像$node这样的变量,你需要在while循环之外声明它们。否则,my 声明会清除前几行中的值。只需将my 行移到while 之前。

      此外,您只想在信息完成后填充散列。将$hash{$e_id} 的作业包装到

      if ($key eq 'eventPlace') {
           ...
      }
      

      【讨论】:

        【解决方案4】:

        Perl 代码算法

        • 通过重新定义$/ = "\n\n" 用输入数据填充@records
        • 将每条记录拆分为%hash
        • %hash 字段重新映射到 %data 哈希以匹配所需的输出
        • 填写%events哈希
        use strict;
        use warnings;
        use feature 'say';
        
        use Data::Dumper;
        
        my @records = do{ $/ = "\n\n"; <DATA> };
        my %events;
        
        for ( @records ) {
            my(%hash,%data);
            %hash = split " : |\n";
            @data{qw/node priority start end/} = @hash{qw/Node Priority startTime endTime/};
            $events{$hash{eventId}} = \%data;
        }
        
        say Dumper(\%events);
        
        __DATA__
        Priority : High
        Node : Node1
        startTime : 2020-08-18T03:40:00
        endTime : 2020-08-18T03:45:00
        eventId : 150
        Text : This is for Node1 text
        eventPlace : Router1
        
        Priority : Medium
        Node : Node2
        startTime : 2020-08-19T00:00:10
        endTime : 2020-08-19T00:00:40
        eventId : 170
        Text : This is for Node2 text
        eventPlace : Router2
        

        输出

        $VAR1 = {
                  '170' => {
                             'start' => '2020-08-19T00:00:10',
                             'end' => '2020-08-19T00:00:40',
                             'node' => 'Node2',
                             'priority' => 'Medium'
                           },
                  '150' => {
                             'node' => 'Node1',
                             'priority' => 'High',
                             'end' => '2020-08-18T03:45:00',
                             'start' => '2020-08-18T03:40:00'
                           }
                };
        

        【讨论】:

        • $/ = "";$/ = "\n\n"; 更有意义。在 perlvar 中搜索“段落模式”。 /// 注意␠:␠不能出现在代码当前形式的数据中。不过,这并不难解决。 (+1)
        猜你喜欢
        • 2020-10-23
        • 2012-12-22
        • 1970-01-01
        • 2016-08-06
        • 2013-06-14
        • 2011-07-23
        • 1970-01-01
        • 2018-12-29
        相关资源
        最近更新 更多