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

I have studied posts at the monastery in an effort to learn to code Perl more professionally. I have created a script to read a flat file that contains TIDs and possible NUMBERs that are associated with the TID. It uses a multidimentionally hash to store values found in a flat file and print them out as they are found however, when I attempt to wait until the flat file is processed to its end and closed, then use a foreach loop to print the hash contents the warning "Use of unitialized value in concatenation (.) or string at ./z line 33." is printed for the sub elements of the hash. Can you help me to understand what is happening to my data between the execution of the while loop and the execution of the foreach loop.

my code

#!/usr/bin/perl -w use strict; # hashes my %Tids=(); # scalars my $Tid=""; # subroutines sub findNumbers; sub findTids; open(IN,"<file"); while(<IN>) { if(/ORIGINATION_ATTEMPT/) { &findTID; print "$Tid,$Tids{\$Tid}{a},$Tids{\$Tid}{b}\n"; } } close(IN); print "============================================\n"; foreach $Tid (keys(%Tids)) { print "$Tid,$Tids{\$Tid}{a},$Tids{\$Tid}{b}\n"; } ## subroutines sub findTID { my $line=(<IN>); if($line =~ /Tid: ([0-9a-f]{8})/) { $Tid = substr($1,3,5); $Tids{$Tid}=$Tid; $Tids{\$Tid}{a}="----------"; $Tids{\$Tid}{b}="----------"; &findNumbers; } } sub findNumbers { my $line=""; while($line !~ /\-{40}/) { $line=<IN>; if($line =~ /FIRST_NUMBER\s+(\d+)\s/) { $Tids{\$Tid}{a}=$1; } if($line =~ /SECOND_NUMBER\s+(\d+)\s/) { $Tids{\$Tid}{b}=$1; } } }

Replies are listed 'Best First'.
Re: multidimensional hash value seems to become unitialized
by graff (Chancellor) on Apr 01, 2011 at 23:47 UTC
    You haven't provided any sample data, but a couple things look strange at first glance.

    First, you have <IN> in three different places: the main "while" condition (which is normal), and in each of your subroutines. If it's true that the input data consists of multiple lines per data "record", it would be a good idea to document this using comments or POD in the code -- otherwise, it tends to look like a mistake.

    Second, you are using a reference to a scalar variable as a hash key: $Tid{\$Tid} and this is almost certainly not what you really want. Curiously, you are sometimes using the actual scalar variable's value as the key, $Tid{$Tid} -- which is more likely what you should be doing all the time.

    How about you try showing a little bit of sample data, along with what the output ought to look like given that input?

    UPDATE: Oh yeah -- the reason for the warning message: You are using a new variable called "$Tid" in the final for loop for printing stuff out, but that's a new variable, so \$Tid (the reference to it) is a new memory address, and nothing has ever been assigned to the hash with that address as the key.

Re: multidimensional hash value seems to become unitialized
by toolic (Bishop) on Apr 01, 2011 at 23:49 UTC
    You are doing a bunch of unusual things in your code. The one that realy jumps out at me is you keep taking a reference to a scalar, for example \$Tid in $Tids{\$Tid}{a}="----------";

    You can try getting rid of the backslash, but I doubt that will solve your problem completely. Here is what I recommend:

Re: multidimensional hash value seems to become unitialized
by Marshall (Canon) on Apr 02, 2011 at 15:06 UTC
    One basic problem is this: $Tids{$Tid}=$Tid; assigns a scalar to $Tids{$Tid} which is not what you want. The value of $Tids{$Tid} will wind up being a hash reference - not a scalar. I made some changes to your code below. Essentially take that line out! I can see from the error messages why you would have been driven to try \$Tid as a key, but that is not the right approach.

    I would not use $Tid as a global. Pass it around when you need it. Check the value of the open of the file named "file" to make sure that it worked. Instead of using "a" and "b", why not use something more descriptive like the text that was in the file, ie FIRST_NUMBER, etc?

    I could make some other comments, but the most important part here is to understand the multi-dimensional hash.

    #!/usr/bin/perl -w use strict; use Data::Dumper; # hashes my %Tids=(); #open(IN,"<file") or die "unable to open file"; while(<DATA>) { if(/ORIGINATION_ATTEMPT/) { my $Tid = findTID(); print "$Tid,$Tids{$Tid}{'FIRST_NUMBER'},", "$Tids{$Tid}{'SECOND_NUMBER'}\n"; } } #close(IN); print "============================================\n"; foreach my $Tid (keys(%Tids)) { print "$Tid,$Tids{$Tid}{'FIRST_NUMBER'},", "$Tids{$Tid}{'SECOND_NUMBER'}\n"; } print Dumper \%Tids; ## subroutines sub findTID { my $line=(<DATA>); if($line =~ /Tid: ([0-9a-f]{8})/) { my $Tid = substr($1,3,5); $Tids{$Tid}{FIRST_NUMBER} ="----------"; $Tids{$Tid}{SECOND_NUMBER}="----------"; findNumbers ($Tid); return ($Tid); } } sub findNumbers { my $Tid = shift; my $line=""; while($line=<DATA>, $line !~ /\-{30,}/) { if($line =~ /FIRST_NUMBER\s+(\d+)\s/) { $Tids{$Tid}{'FIRST_NUMBER'}=$1; } if($line =~ /SECOND_NUMBER\s+(\d+)\s/) { $Tids{$Tid}{'SECOND_NUMBER'}=$1; } } } =prints ############################### d1234,9876,9999 a4321,1234,---------- a9999,----------,3333 ============================================ a9999,----------,3333 a4321,1234,---------- d1234,9876,9999 $VAR1 = { 'a9999' => { 'SECOND_NUMBER' => '3333', 'FIRST_NUMBER' => '----------' }, 'a4321' => { 'SECOND_NUMBER' => '----------', 'FIRST_NUMBER' => '1234' }, 'd1234' => { 'SECOND_NUMBER' => '9999', 'FIRST_NUMBER' => '9876' } }; =cut ###################################### __DATA__ --------ORIGINATION_ATTEMPT-------- Tid: abcd1234 FIRST_NUMBER 9876 SECOND_NUMBER 9999 --------------------------------------- --------ORIGINATION_ATTEMPT-------- Tid: defa4321 FIRST_NUMBER 1234 --------------------------------------- --------ORIGINATION_ATTEMPT-------- Tid: defa9999 SECOND_NUMBER 3333 ---------------------------------------