On Jan 15, 2004, at 8:30 PM, Kenton Brede wrote:

Thanks for the great explanation. I've been working with the code above
since I need more hash practice, trying to keep on track with the
original poster's question. The code below works fine except I can't
figure out how to put one "\n" between the two records like -


Name: Bob
City: Austin
State: Texas

Name: Jose
City: Denver
State: Colorado

If I place "print "\n";" after the print line I get double spaces
between all lines.  If I place it outside the last "for" loop I get
double spaces between the two records.  What I have below just prints
them in one block.  Hope that all made some sense:)

I'm with you. See below.


Thanks for any help,
Kent

#!/usr/bin/perl
use warnings;
use strict;

You forgot a very important line from my example right here:


local $/ = ''; # enter "paragraph" mode

Your code was reading line by line, then working on just that line. You never had more than one entry in the hash at a time.

My example reads until it sees one or more blanks lines (paragraph mode), then works with an entire contact at once.

That almost fixes you up, but we have to do a little more.

while (<DATA>) {
    my @lines = (split /\n/, $_);
    my %contact;
    for my $line (@lines) {
        if ($line =~ /^(\w+):\s*(.+)$/) {
             $contact{$1} = $2;

The for loop below is in the wrong spot. Here we're processing each line of the hash, so the loop would get run once for each and every line, giving us erroneous output. We need to move it down a bit...


             for (keys %contact) {
                 if (/^Name/ or /^City/ or /^State/) {

Minor complaint about the line above. Don't use a Regex to test equality. Save the big guns for when you actually need pattern matching. It should read:


if ($_ eq 'Name' || $_ eq 'City' || $_ eq 'State') {

                    print "$_: $contact{$_}\n";
                 }
             }
        }
    }

Move that for loop I mentioned above to here and add a:


print "\n";

after the loop. That "fixes" your program. Let's look into it a little more though...

}

Putting everything I've said so far together, we get the code:


#!/usr/bin/perl

use warnings;
use strict;

local $/ = '';

while (<DATA>) {
    my @lines = (split /\n/, $_);
    my %contact;
    for my $line (@lines) {
        if ($line =~ /^(\w+):\s*(.+)$/) {
             $contact{$1} = $2;
        }
    }
         for (keys %contact) {
                 if ($_ eq 'Name' || $_ eq 'City' || $_ eq 'State') {
                        print "$_: $contact{$_}\n";
                 }
         }
         print "\n";
}

__DATA__
Name: Bob
City: Austin
State: Texas
Address: 123 Whatever
Age: 46

Name: Jose
City: Denver
State: Colorado
Address: 118 Mystreet
Age: 28

Go ahead and paste that into a file and run it a few times for some interesting results.

Do you see it? The order of the output changes with subsequent runs if you're running a fairly recent version of perl.

I don't think it was ever said if order is significant in this problem, but the behavior is annoying so let's fix it too. The "problem" is in the output loop:

         for (keys %contact) {
                 if ($_ eq 'Name' || $_ eq 'City' || $_ eq 'State') {
                        print "$_: $contact{$_}\n";
                 }
         }

That loop fetches all the keys of the hash and then prints the Name, City and State as it finds them. Trouble is, perl isn't guaranteed to return those keys in a set order and indeed it doesn't. The loop is also wasteful as it has to go through keys we don't care about. Let's change it to ask for what we want instead:

         for (qw(Name City State)) {
                print "$_: $contact{$_}\n";
         }

That does it. The program now returns what we expect every time it's run.

Hope that clears things up for you.

James


-- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] <http://learn.perl.org/> <http://learn.perl.org/first-response>




Reply via email to