Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

How to read data file into an array of hashes

by fseng (Novice)
on Jun 23, 2009 at 00:39 UTC ( #773807=perlquestion: print w/ replies, xml ) Need Help??
fseng has asked for the wisdom of the Perl Monks concerning the following question:

Hi guys, I'm just starting learning Perl. Here is my Q.
I want to write a program to read in the following fixed width data file into a data structure of a real array of hashes, the data file for example called "clients" is like following:
Surname L20, First Name L20, Town L20 -------------------------------------------------- Chaplin Charlie Basel Estevez Emilio Santa Manica Sarte Jean Paul Montmarte Rikard Frank Amsterdam Rodin Paul Montmarte
How should I do this?I want to read them in and sorted them in to associated hash where you can find who lives in diff towns or where Chaplin lives etc. I only came up with this so far:
open (CL, "clients"); my $list; while (<CL>){ chomp; print "$list\n"; }
Please help, it's urgent too....

Comment on How to read data file into an array of hashes
Select or Download Code
Re: How to read data file into an array of hashes
by toolic (Chancellor) on Jun 23, 2009 at 01:15 UTC
    it's urgent too....
    Then I hope you're an Evelyn Wood graduate: perlpacktut

    The following assumes unique surnames. This gets your fixed-width information into an unsorted Perl data structure (hash-of-hashes). sort it as you see fit:

    use strict; use warnings; use Data::Dumper; my %clients; while (<DATA>) { next if /^Surname/; next if /^-/; my ($surname, $firstname, $town) = unpack 'A13A16A20', $_; $clients{$surname} = {first => $firstname, town => $town}; } print Dumper(\%clients); __DATA__ Surname L20, First Name L20, Town L20 -------------------------------------------------- Chaplin Charlie Basel Estevez Emilio Santa Manica Sarte Jean Paul Montmarte Rikard Frank Amsterdam Rodin Paul Montmarte

    prints:

    $VAR1 = { 'Rodin' => { 'first' => 'Paul', 'town' => 'Montmarte' }, 'Rikard' => { 'first' => 'Frank', 'town' => 'Amsterdam' }, 'Chaplin' => { 'first' => 'Charlie', 'town' => 'Basel' }, 'Sarte' => { 'first' => 'Jean Paul', 'town' => 'Montmarte' }, 'Estevez' => { 'first' => 'Emilio Santa', 'town' => 'Manica' } };
      Hi guys, Thank you so much. All I can say is that why did I spend the past 2 days trying to figure it out myself??!! I should come here earlier. I prefer the unpack one better since I feel dissy when I see all the regular expressions....
        All I can say is that why did I spend the past 2 days trying to figure it out myself??!! I should come here earlier.
        No, what you did is correct, you have to try out something of yourself first, otherwise you wouldn't have had the taste of regular expressions in that process.
        moreover IMO, pack/unpack are not easier ones to understand and use for any Perl newbies!.
        Good that you have got your solution, but never forget to try yourself and then come here for clarifications; we love to clarify such doubts.


        Vivek
        -- 'I' am not the body, 'I' am the 'soul', which has no beginning or no end, no attachment or no aversion, nothing to attain or lose.
Re: How to read data file into an array of hashes
by bichonfrise74 (Vicar) on Jun 23, 2009 at 01:49 UTC
    Another possible solution?
    #!/usr/bin/perl use strict; use Data::Dumper; my %HoH; while (<DATA>) { next if (/^Surname/ or /^--/); s/(\w+)\s+(\w+\s?\w+?)\s+(\w+)//; $HoH{$1} = { $2 => $3 }; } print Dumper \%HoH; __DATA__ Surname L20, First Name L20, Town L20 -------------------------------------------------- Chaplin Charlie Basel Estevez Emilio Santa Manica Sarte Jean Paul Montmarte Rikard Frank Amsterdam Rodin Paul Montmarte
Re: How to read data file into an array of hashes
by graff (Chancellor) on Jun 23, 2009 at 02:50 UTC
    Contrary to toolic's suggestion, I think you are right to try for an array of hashes, because you probably can't count on the "last name" field to be unique across all records.

    So, to build on the otherwise fine code of the first reply:

    use strict; use warnings; use Data::Dumper; my @clients; # make this an array, not a hash; my @fields = qw/surname firstname town/; while (<DATA>) { next if ( /^Surname/ or /^-/ ); my @values = unpack 'A13A16A20', $_; my %entry = map { $fields[$_] => $values[$_] } ( 0..$#fields ); push @clients, \%entry; } print Dumper(\@clients); __DATA__ Surname L20, First Name L20, Town L20 -------------------------------------------------- Chaplin Charlie Basel Estevez Emilio Santa Manica Sarte Jean Paul Montmarte Rikard Frank Amsterdam Rodin Paul Montmarte

    (update: it does seem odd that the field widths in the column headings do not match the actual widths used in the data and in the unpack spec. It would be better if the headings were consistent with the data, but that's a minor point...)

      Thanks guys. But now I come up with one more question. Now I want to print all the lines sorted by the Town. Like this
      Town Surname Firstname Basel Chaplin Charlie Manica Estevez Emilio Santa Montmarte Sarte Jean Paul Montmarte Rodin Paul Amsterdam Rikard Frank
      I did something like this:
      my $data = "clients.dat"; open (DAT, $data) or die "\"$data\" not existed or can't be opened!\n" +; my @client; # make this an array, not a hash; my @fields = qw/surname firstname town/; while (<DAT>) { next if ( /^Surname/ or /^-/ ); my @values = unpack 'A20A20A20', $_; my %entry = map { $fields[$_] => $values[$_] } ( 0..$#fields ); push @client, \%entry; } my @towns; my $client; foreach my $client (@client){ push @towns, "$client->{'town'}"; } my @sorted_towns = sort @towns; foreach my $key (@sorted_towns) { my @elems = $key; my $client = $client->[$elems[1]-1]; print "$client->{'town'} $client->{'surname'} $client->{'firstname +'}"; }
      But this doesnt work somehow. Can anyone help please?
        You don't need (or want) to sort the town names into a separate array; after the AoH is loaded, you just need to loop over the list of sorted array elements, where the sorting is based on the "town" value, like this:
        for my $entry ( sort { $a->{town} cmp $b->{town} } @client ) { printf "%s\t%s\t%s\n", $entry->{town}, $entry->{surname}, $entry-> +{firstname}; }
        (not tested)

        (updated to add missing "->" in the printf statement, based on fseng's reply below)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://773807]
Approved by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (19)
As of 2014-07-10 13:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (210 votes), past polls