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

I've gotten my Array of Hashes built the way I want it. However, I'm running into a small problem when I turn strict on. I get the message:
Can't use string ("XXX") as a HASH ref while 
"strict refs" in use at test.pl line 20, <LOG> line 1.

Here is the code I am looking at.

use strict; use warnings; my (@unix_data,@nt_data); open LOG, "nt_data.txt" or die "Cannot open unix_data.txt $!"; while (<LOG>) { chomp; my $hashname; my @keysandvalues; my @field = split /,/, $_; foreach (@field) { my ($key, $value) = split /\s*=\s*/, $_; if ($key =~ /^(LocalTran)(\w*)(Number)$/) { $hashname=$value; } push @keysandvalues, $key; push @keysandvalues, $value; } %{$hashname}=@keysandvalues; push @nt_data,\%{$hashname}; } foreach (@nt_data) { my $key; print "-+" x 90 . "\n"; foreach $key (keys %{$_}) {print "$key => ${$_}{$key}\n";} }

Of course it works properly when I set 'no strict "refs";' in the block, but I don't want to do that, I want to find out why I am running into this problem. I am currently reading through the Camel Book to find a solution, but thought I'd post it here as well to see if I can't learn something. Thanks in advance. I'm sure the answer is quite simple but I can't quite get my head around it right now.


"Ex libris un peut de tout"

Replies are listed 'Best First'.
Re: Problem with strict "refs"
by Limbic~Region (Chancellor) on Sep 22, 2003 at 19:54 UTC
    nimdokk,
    Take a look at the line giving the error:
    %{$hashname}=@keysandvalues;
    You are using a variable as a variable name. You should probably read why that is usually considered a bad idea. A quick way to get around this is to change your data structure into a HoH. This way the hash name itself is not being created dynamically, but the keys can be.
    my %hash; $hash{$hashname} = { @keysandvalues };

    Cheers L~R

    Update: I thought I would go beyond answering your question and point out a few other things as well.

  • You should indent inside blocks as it makes it easier to read and ensure you don't have mismatched braces
  • my ($key, $value) = split /\s*=\s*/, $_; should probably be using the 3 arg form of split. see perldoc -f split
  • if ($key =~ /^(LocalTran)(\w*)(Number)$/) { $hashname=$value; } is likely a mistake. You are either using capturing parens for no reason or if you mean to match literal parens you should escape them \(
  • $hashname=$value assumes that you will only match one time in your loop. This might be safe, it might not be. If you match more than once, you will clobber a would be hash.
  • {print "$key => ${$_}{$key}\n";} will also be causing a sym ref error with strictures

    I am sure there are some other things, but this should certainly get you started.

Re: Problem with strict "refs"
by dragonchild (Archbishop) on Sep 22, 2003 at 19:59 UTC
    strict 'refs' is wisely telling you that you're using symbolic references. You're setting $hashname to some string and then making a hash with that name. This is a "Bad Thing"(tm).

    I suspect it will be easier to do the following:

    use strict; use warnings; use IO::File; my $filename = 'nt_data.txt'; my $fh = IO::File->new($filename) || die "Cannot open '$filename' for reading: $!\n"; my @nt_data; while (<$fh>) { chomp; my %x; foreach (split /,/) { my ($k, $v) = split /\s*=\s*/; # This line becomes unnecessary ... you weren't using $hashname anyway +s ... # if ($key =~ /^(LocalTran)(\w*)(Number)$/) { $hashname=$value; +} $x{$k} = $v; } push @nt_data, \%x; } $fh->close;

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

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Problem with strict "refs"
by jeffa (Bishop) on Sep 22, 2003 at 19:54 UTC

    Yes. It is good that you do not want to turn of strict, because that will not solve your problem. As it stands, a good answer to your problem is dependent upon the data you are dealing with. Would you please remember to post some example data ... not just the script, next time? It would be most helpful.

    Your error means that you are trying to access something that isn't there - you are trying to access something as a hash ref when it isn't. Again, we could sit here all day and guess at exactly where you faulted ... but if you post the data, we can find the problem much faster.

    UPDATE:
    Limbic~Region++ ... that line did look very suspicious. But please show us the data! I guarantee that someone will show a more consise way to parse it. ;) (heh heh ... looks like dragonchild already did it!)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Problem with strict "refs"
by fletcher_the_dog (Friar) on Sep 22, 2003 at 20:04 UTC
    Your main problem is that your are trying to create a hash by directly accessing the symbol table for hashes. This is not usually something you should do unless you know what you are doing and in fact is guarded against by using "use strict". You can do the same thing that you are trying to do by create a hash that holds your "LocalTran" types". Like This:
    use strict; use warnings; my %LocalTrans; my (@unix_data,@nt_data); open LOG, "nt_data.txt" or die "Cannot open unix_data.txt $!"; while (<LOG>) { chomp; my $hashname; my @keysandvalues; my @field = split /,/, $_; foreach (@field) { my ($key, $value) = split /\s*=\s*/, $_; if ($key =~ /^(LocalTran)(\w*)(Number)$/) { $hashname=$value; } push @keysandvalues, $key; push @keysandvalues, $value; } %{$LocalTrans{$hashname}}=@keysandvalues; } foreach my $LT (keys %LocalTrans) { print "-+" x 90 . "\n"; foreach my $key (keys %{$LocalTrans{$LT}) { print "$key => $LocalTrans{$LT}{$key}\n"; } }
Re: Problem with strict "refs"
by nimdokk (Vicar) on Sep 23, 2003 at 11:59 UTC
    Thanks for the comments. It gives me a direction to go in. Since this is really a side project, I'm not devoting a lot of time and energy to it, but it will be helpful in the near future because we have a lot of data to crunch from log files.

    Here is a sample of what the data might look at in its rawest form (note, the data looks slightly different depending on its source, W2K or Unix).

    __DATA__ Field1=Value1,Field2=Value2,Field3=,Field4=Value4 Field1=Value1,Field2=Value2,Field3=,Field4=Value4 Field1=Value1,Field2=Value2,Field3=,Field4=Value4
    And so on. The Field names might look slightly different depending on the originating platform. I'm sure there is a better way to do this, but I'm still learning.


    "Ex libris un peut de tout