Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi

I have the following data

Date: Mon Feb 17 13:14:19 2003 SServer: formula3 Device: 111.222.123.124 CString: public Port: 162 SNMP Traps Generated: Trap OID: 1.3.6.1.4 Generic: 2 Specific: 0 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 10 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 20 Varbind: 1.3.6.1.2.1.2.2.1.7 Type: 2 Data: 20 Varbind: 1.3.6.1.2.1.2.2.1.8 Type: 2 Data: 30 Date: Mon Feb 17 13:14:22 2003 SServer: formula3 Device: 111.164.121.125 CString: public Port: 162 SNMP Traps Generated: Trap OID: 1.3.6.1.4 Generic: 3 Specific: 0 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 12 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 20
I need to reference each block and all of its contents. I was thinking about using a hash keyed by a given number, and have the contents an array that I could reference easily.
Does anyone else have any better suggestions (or more efficient?)
Thanks

Replies are listed 'Best First'.
Re: recomendations needed for type of data structure
by steves (Curate) on Feb 17, 2003 at 19:01 UTC

    Looks like an array of hashes to me. Something like this (you can finish it if you use it):

    use strict; my $hash; my $trap; my @data; while (<DATA>) { chomp; next if ($_ =~ /^\s*$/); if ($_ =~ /^Date:\s+(.+)$/) { push(@data, $hash) if (defined($hash)); $hash = {DATE => $1}; } if ($_ =~ /^SServer:\s+(.+)$/) { $hash->{SSERVER} = $1; } elsif ($_ =~ /^Device:\s+(.+)$/) { $hash->{DEVICE} = $1; } elsif ($_ =~ /^CString:\s+(.+)$/) { $hash->{CSTRING} = $1; } elsif ($_ =~ /^Port:\s+(.+)$/) { $hash->{PORT} = $1; } elsif ($_ =~ /^SNMP Traps Generated:/) { $hash->{TRAPS} = {TRAP_LIST => []}; } elsif ($_ =~ /^\s+Generic:\s+(.+)$/) { $hash->{TRAPS}->{GENERIC} = $1; } elsif ($_ =~ /^\s+Specific:\s+(.+)$/) { $hash->{TRAPS}->{SPECIFIC} = $1; } elsif ($_ =~ /^\s+Varbind:\s+(.+)$/) { $trap = {VARBIND => $1}; } elsif ($_ =~ /^\s+Type:\s+(.+)$/) { $trap->{TYPE} = $1; } elsif ($_ =~ /^\s+Data:\s+(.+)$/) { $trap->{DATA} = $1; push(@{$hash->{TRAPS}->{TRAP_LIST}}, $trap); } } push(@data, $hash) if (defined($hash)); my ($item, $key, $value); foreach $item (@data) { while (($key, $value) = each %$item) { print "$key => $value\n"; } print "\n"; } __DATA__ Date: Mon Feb 17 13:14:19 2003 SServer: formula3 Device: 111.222.123.124 CString: public Port: 162 SNMP Traps Generated: Trap OID: 1.3.6.1.4 Generic: 2 Specific: 0 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 10 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 20 Varbind: 1.3.6.1.2.1.2.2.1.7 Type: 2 Data: 20 Varbind: 1.3.6.1.2.1.2.2.1.8 Type: 2 Data: 30 Date: Mon Feb 17 13:14:22 2003 SServer: formula3 Device: 111.164.121.125 CString: public Port: 162 SNMP Traps Generated: Trap OID: 1.3.6.1.4 Generic: 3 Specific: 0 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 12 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 20
      Thanks,
      That' swhat I had in mind.
      Just one question,
      Do you know why the array for the Traps isn't printing out properly?
      It's printing out the array reference instead
      Thanks
        This is because the array of traps is stored as a reference to an anonymous array in one of the keys (TRAP_LIST) of the anonymous hash in the TRAPS key of the %$hash. This means that when you try to print the keys and values of the %{$hash->{TRAPS}} hash, you get the stringified array reference. To solve this you could add a special case to the output loop which prints the contents of the array.
        # this handles ARRAYs and HASHes # you may want to use Data::Dumper for debugging however # the final code for processing the data structure will # also be less generic than this code. if (ref $value eq 'ARRAY') { print "$key => (@$value)\n"; } elsif (ref $value eq 'HASH') { print "$key => (%$value)\n"; } else print "$key => $value\n"; }

        --
        integral, resident of freenode's #perl
        
      Looks real good. A few style comments, if you will.

      If you're using $_, you can just say if (/blahblah/) { instead of if ($_ =~ /blahblah/) {. $_ is the default 'it' ... use it as such.

      If it wasn't for the traps thing, you could write everything as:

      while (<DATA>) { chomp; next unless /^(\S+):\s+(.+)\s*$/; my ($key, $val) = (uc $1, $2); if ($key eq 'DATE') { push @data, $hash if defined $hash; $hash = {}; } $hash->{$key} = $val; }
      But, because of the 'SNMP Traps Generated' string, that doesn't work.

      But, can we make it work?

      use strict; my $hash = undef; my $trap = undef; my @data = (); while (<DATA>) { chomp; next unless /^\s*(\S+):\s+(.+)\s*$/o || /^\s*Trap (\S+):\s+(.+)\s*$/o || /^\s*SNMP (\S+) Generated:\s*$/o; my ($key, $val) = (uc $1, $2); if ($key eq 'DATE') { if (defined $hash) { if (defined $trap) { push @{$hash->{TRAPS}{TRAP_LIST}}, $trap; $trap = undef; } push @data, $hash if defined $hash; } $hash = {}; } elsif ($key eq 'TRAPS') { $hash->{TRAPS} = { TRAP_LIST => [] }; $trap = {}; next; } if ($key eq 'GENERIC' || $key eq 'SPECIFIC' || $key eq 'OID') { $hash->{TRAPS}{$key} = $val; } elsif (defined $trap) { $trap->{$key} = $val; if ($key eq 'DATA') { push @{$hash->{TRAPS}{TRAP_LIST}}, $trap; $trap = {}; } } else { $hash->{$key} = $val; } } if (defined $hash) { if (defined $trap) { push @{$hash->{TRAPS}{TRAP_LIST}}, $trap; $trap = undef; } push @data, $hash if defined $hash; } use Data::Dumper; print Data::Dumper->Dump([\@data]); __DATA__ Date: Mon Feb 17 13:14:19 2003 SServer: formula3 Device: 111.222.123.124 CString: public Port: 162 SNMP Traps Generated: Trap OID: 1.3.6.1.4 Generic: 2 Specific: 0 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 10 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 20 Varbind: 1.3.6.1.2.1.2.2.1.7 Type: 2 Data: 20 Varbind: 1.3.6.1.2.1.2.2.1.8 Type: 2 Data: 30 Date: Mon Feb 17 13:14:22 2003 SServer: formula3 Device: 111.164.121.125 CString: public Port: 162 SNMP Traps Generated: Trap OID: 1.3.6.1.4 Generic: 3 Specific: 0 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 12 Varbind: 1.3.6.1.2.1.2.2.1.1 Type: 2 Data: 20
      Why do it this way? If the data adds new things to track, it's easier to handle them. Plus, this way is quicker. While that's often not a big issue, what if this script is to be run every 15 minutes on a huge server? It does no good if the script takes 16 minutes to run ...

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.