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

I am trying to read a line of text from a file, and use it to create an array called @list so that I can access the individual elements. The text file is called testhash.txt and at present contains a single line of text that reads:
'key1,value1','key2,value2','key3,value3','key4,value4'

The script behaves as if @list is a single line of text rather than an array. Yet if I simply copy and paste the line of text into @list like so:

@list=('key1,value1','key2,value2','key3,value3','key4,value4');

then I can do arrayish things with it. Here is my script:

#testhash.txt has a single line of text that reads: #'key1,value1','key2,value2','key3,value3','key4,value4' open(HASH,"testhash.txt"); $line=(<HASH>); chomp $line; @list=($line); @list=('key1,value1','key2,value2','key3,value3','key4,value4'); print "here is \@list:\n"; print $list[0]; print $list[3]; print "\nEnter the keycode:"; chop ($find=<STDIN>); foreach (@list) { ($keycode,$keyvalue)=split /,/; #split up the array elements if ($find=~/$keycode/i) { print "Keycode $keycode has the value $keyvalue \n"; } }
Any help would be much appreciated because I'm clearly missing some vital point here!

Replies are listed 'Best First'.
Re: How can I read a line of text from a file into an array?
by Cubes (Pilgrim) on Jul 16, 2001 at 22:09 UTC
    It looks like you are confusing text read in from a file with text that's part of your program's code. The latter is interpreted by the perl compiler; the former just gets shoved into the variable.

    There's More Than One Way To Do It. If you really trust your input data, you could stick it inside an eval().

    What you probably want to do, though, is read up on split(). From the example you provided, you'll probably want something a bit more sophisticated. Try searching for 'CSV' at CPAN

      Yeah, since you are using quoted text with contained commas I would certainly look into the CSV modules on CPAN...

      or get rid of the quotes and as long as your keys and values don't contain any commas you could do...

      $line = 'key1,value1,key2,value2'; my %foo = split ',', $line; print $foo{key2}; #prints the value associated with key2, i.e. value2

                      - Ant

Re: How can I read a line of text from a file into an array?
by Donesh (Acolyte) on Jul 16, 2001 at 22:14 UTC
    Hello,

    Try using some of the code below. A note of caution is that using map will create a 2d array. If you only have one line in the file they will all apear in the 0th element of the first dimension of the array. I hope this helps.

    Donesh

    Code:

    sub getdata { my $filename = "<" . shift; open (HANDLE,$filename) or die "Cant Open $filename: $!"; my @final = map{chomp; [split/,/]} <HANDLE>; close HANDLE; return (@final); }
      This code makes unnecessary use of an anonymous array making handling of the returned values more involved than necessary. It also retains the single-quotes in the data resulting in this set of values:
      'key1 value1' 'key2 value2' 'key3 value3' 'key4 value4'
Re: How can I read a line of text from a file into an array?
by dvergin (Monsignor) on Jul 16, 2001 at 22:55 UTC
    As others have commented here, you have a deceptively tricky bit of work here to parse out this data because of the commas both inside and outside the quotes. If you have control over the format of testhash.txt you should consider changing that. An easy-to-parse format would be to separate key-value sets each on their own line. Thus:
    key1,value1 key2,value2 key3,value3 key4,value4
    On the other hand if you need to use the file as it is, and if you know it will always have exactly four key-value pairs in the format you have given, this code will allow you to parse it into a hash.
    open HANDLE, 'testhash.txt' or die "Can't open: $!"; my $data = <HANDLE>; close HANDLE; chomp $data; my %hash = $data =~ /'([^,]+),([^']+)', '([^,]+),([^']+)', '([^,]+),([^']+)', '([^,]+),([^']+)'/x; # demo use of the hash to access results foreach my $key (keys %hash) { print "$key $hash{$key}\n"; }
    It ain't elegant or flexible, but it will satisfy your immediate need. If there are going to be spaces after the commas or other interesting things, that can be dealt with in this approach. But at a certain point, you would need to consider a more robust approach (i.e. a CSV module from CPAN). Which would be a bit of overkill just to grab four key-value pairs.
Re: How can I read a line of text from a file into an array?
by dvergin (Monsignor) on Jul 16, 2001 at 23:50 UTC
    Okay, here's a more perlish, robust solution that will handle any number of key-value pairs. This code plays nice and spells out each step:
    open HANDLE, 'testhash.txt' or die "Can't open: $!"; my $data = <HANDLE>; close HANDLE; chomp $data; my %hash; foreach my $pair (split /'/, $data) { next if $pair eq ''; # empty 'item' before first comma next if $pair =~ /^,$/; # throw away commas between pairs my ($key, $value) = split /,/, $pair; $hash{$key} = $value; } # demo use of the hash to access results foreach my $key (keys %hash) { print "$key $hash{$key}\n"; }
    Or for a compact solution using the same logic. You could replace everything after the chomp with: my %hash = map {split /,/} grep !/^,$/, split /'/, $data;
      Brilliant! That's helped enormously, thanks. Now for the real file which has several hundred lines and 10 key value pairs :)
        In that case (and you may already have this worked out) if you are building one great big hash, the main code would look something like:
        open HANDLE, 'testhash.txt' or die "Can't open: $!"; my %hash; for my $data (<HANDLE>) { chomp $data; foreach my $pair (split /'/, $data) { next if $pair eq ''; next if $pair =~ /^,$/; my ($key, $value) = split /,/, $pair; $hash{$key} = $value; } } close HANDLE; # do stuff with entire hash here
        Or if you are doing something with the hash line-by-line:
        open HANDLE, 'testhash.txt' or die "Can't open: $!"; for my $data (<HANDLE>) { chomp $data; my %hash; foreach my $pair (split /'/, $data) { next if $pair eq ''; next if $pair =~ /^,$/; my ($key, $value) = split /,/, $pair; $hash{$key} = $value; } # do interesting things with one-line %hash here } close HANDLE;