LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Trying to get Understanding on filehandle and split function in Perl (https://www.linuxquestions.org/questions/programming-9/trying-to-get-understanding-on-filehandle-and-split-function-in-perl-913081/)

jmc1987 11-11-2011 05:01 PM

Trying to get Understanding on filehandle and split function in Perl
 
Okay I'm still working on my perl studies when I can and I am a little confused on how this is working.

PHP Code:

#!/usr/bin/perl -w

open(PH"customers.txt") or die "Cannot open customers.txt: $!\n";
while(<
PH>) {
    
chomp;
    (
$number$email) = (split(/\s+/, $_))[1,2];
    
$Phone{$number}=$_;
    
$Email{$email}=$_;
}
close(PH);

print 
"Type 'q' to exit\n";
while (
1) {
    print 
"\nNumber? ";
    
$number = <STDIN>; chomp ($number);
    
$address "";
    if (! 
$number) {
        print 
"E-Mail? ";
        
$address = <STDIN>; chomp $address;
    }
    
    
next if (! $number and ! $address);
    
last if ($number eq 'q' or $address eq 'q');
    
    if (
$number and exists $Phone{$number} ) {
        print 
"Customer: $Phone{$number}\n";
        
next;
    }
    if (
$address and exists $Email{$address} ) {
        print 
"Customer: $Email{$address}\n";
        
next;
    }
    print 
"Customer record not found. \n";
    
next;
}
print 
"\nAll done.\n"

customers.txt
Code:

Smith,John    (248)-555-9430 jsmith@aol.com
Hunter,Apryl    (810)-555-3029 april@showers.org
Stewart,Pat    (405)-555-8710 pats@starfleet.co.uk
Ching,Iris    (305)-555-0919 iching@zen.org
Doe,John    (212)-555-0912 jdoe@morgue.com
Jones,Tom    (312)-555-3321 tj2342@aol.com
Smith,John    (607)-555-0023 smith@pocahontas.com
Crosby,Dave    (405)-555-1516 cros@csny.org
Johns,Pam    (313)-555-6790 pj@sleepy.com
Jeter,Linda    (810)-555-8761 netless@earthlink.net
Garland,Judy    (305)-555-1231 ozgal@rainbow.com

Now what I have to do is modify this code to allow me to search by a name and I'm not quite understanding on how the names are stored in the hashes.

Now this is where I'm not quite understanding on line 4,5,and 6.
PHP Code:

open(PH"customers.txt") or die "Cannot open customers.txt: $!\n";
while(<
PH>) {
    
chomp;
    (
$number$email) = (split(/s+/, $_))[1,2];
    
$Phone{$number}=$_;
    
$Email{$email}=$_;
}
close(PH); 

Now from what I understand
line 1: open customers.txt for reading
line 2: begins the loop untill it run out of lines in customers.txt
line3: removes all the new lines off of each line
line 4: take something like
Quote:

Smith,John (248)-555-9430 jsmith@aol.com
($number, $email) = (split(/s+/, $_))[1,2];
now from what I understand the split is splitting on pattern whie spaces to the end of line and [1,2] Is 1 for the number and 2 for the email. So does that mean 0 would be for name?

Really I just need a better explaination on how line 2-6 work out so I can modify the code to search by name.

lesleyb 11-11-2011 05:33 PM

You might need to get [0] , split that on the , amd then match on the result

jmc1987 11-11-2011 07:11 PM

Building a name search function into perl program
 
grr I can't edit the title of the post.

Okay I got this code allow me to search by name but I have to put a whitespace in front of all lines in customers.txt

PHP Code:

#!/usr/bin/perl -w

open(PH"customers.txt") or die "Cannot open customers.txt: $!\n";
while(<
PH>) {
    
chomp;
    (
$name$number$email) = (split(/\s+/, $_))[1,2,3];
    
$Name{$name}=$_;
    
$Phone{$number}=$_;
    
$Email{$email}=$_;
}
close(PH);

print 
"Type 'q' to exit\n";
while (
1) {
    print 
"\nNumber? ";
    
$number = <STDIN>; chomp ($number);
    
$address "";
    
$name "";
    if (! 
$number) {
        print 
"E-Mail? ";
        
$address = <STDIN>; chomp $address;
        
        if ( ! 
$address) {
            print 
"Name? ";
            
$name = <STDIN>; chomp $name;
        }
        
    }
    
    
next if (! $number and ! $address and ! $name);
    
last if ($number eq 'q' or $address eq 'q' or $name eq 'q');
    
    if (
$number and exists $Phone{$number} ) {
        print 
"Customer: $Phone{$number}\n";
        
next;
    }
    if (
$address and exists $Email{$address} ) {
        print 
"Customer: $Email{$address}\n";
        
next;
    }
    if (
$name and exists $Name{$name} ) {
        print 
"Customer: $Name{$name}\n";
        
next;
    }
    print 
"Customer record not found. \n";
    
next;
}
print 
"\nAll done.\n"

But if I was to be graded on the modification I don't think I would pass because the exercise says
Quote:

Modify the Customer program to allow a search by name, Because you cannot use name as a hash key, you need to search through the values in the hash.
So I guess I'm back to figure it out how the book says to.

SecretCode 11-12-2011 06:22 AM

Code:

($name, $number, $email) = (split(/s+/, $_))[1,2,3];
Close. Firstly you need /\s+/ to split on spaces - currently you are splitting in literal 's'. Secondly, arrays are indexed from zero so you need [0,1,2] ... but since this is the entire array you don't in fact need to take a slice.
Code:

($name, $number, $email) = (split(/\s+/, $_));
Now, you can't use name as a hash key because name might not be unique (and isn't unique in the sample data!). This means that your new code will fail on 'Smith,John'.

So you need something like
Code:

$Names{$number} = $name;
in your file-read loop, and
Code:

    if ($name) {
            my $found = 0;
            while (my ($tryphone, $tryname) = each(%Names)) {
                if ($name eq $tryname) {
                    print "Customer: $Phone{$tryphone}\n";
                $found = 1;
                }
            }
        next if $found;
    }

in your interactive loop. Read about iterating over hashes: iterate through a hash | Perl HowTo and lots of other sites.

The original code uses email and phone as keys for two different hashes. I would say this is bad because you could have two customers with the same phone number (different people in the same building). Anyway it makes more sense to have a single hash for a single object - but you'd need to get to grips with hashes of hashes.

jmc1987 11-12-2011 10:57 AM

Actually I got it to work last night with this
PHP Code:

#!/usr/bin/perl -w

open(PH"customers.txt") or die "Cannot open customers.txt: $!\n";
while(<
PH>) {
    
chomp;
    (
$name$number$email) = (split(/\s+/, $_))[0,1,2];
    
$Name{$name}=$_;
    
$Phone{$number}=$_;
    
$Email{$email}=$_;
}
close(PH);

print 
"Type 'q' to exit\n";
while (
1) {
    print 
"\nNumber? ";
    
$number = <STDIN>; chomp ($number);
    
$address "";
    
$name "";
    if (! 
$number) {
        print 
"E-Mail? ";
        
$address = <STDIN>; chomp $address;
        
        if (! 
$address) {
        print 
"Name? ";
        
$name = <STDIN>; chomp $name;
        }        
    }
    
    
next if (! $number and ! $address and ! $name);
    
last if ($number eq 'q' or $address eq 'q' or $name eq 'q');
    
    if (
$number and exists $Phone{$number} ) {
        print 
"Customer: $Phone{$number}\n";
        
next;
    }
    if (
$address and exists $Email{$address} ) {
        print 
"Customer: $Email{$address}\n";
        
next;
    }
    if (
$name and exists $Name{$name} ) {
        print 
"Customer: $Name{$name}\n";
        
next;
    }
    print 
"Customer record not found. \n";
    
next;
}
print 
"\nAll done.\n"

customers.txt
Code:

Smith,John    (248)-555-9430 jsmith@aol.com
Hunter,Apryl    (810)-555-3029 april@showers.org
Stewart,Pat    (405)-555-8710 pats@starfleet.co.uk
Ching,Iris    (305)-555-0919 iching@zen.org
Doe,John    (212)-555-0912 jdoe@morgue.com
Jones,Tom    (312)-555-3321 tj2342@aol.com
Smith,John    (607)-555-0023 smith@pocahontas.com
Crosby,Dave    (405)-555-1516 cros@csny.org
Johns,Pam    (313)-555-6790 pj@sleepy.com
Jeter,Linda    (810)-555-8761 netless@earthlink.net
Garland,Judy    (305)-555-1231 ozgal@rainbow.com

So I know longer needed the whitespaces in front of each line in customers.txt. But I will try the while loop to search like you showed. I do see your point where I don't need the [0,1,2] since I'm returning the whole thing into a hash.

Thanks for the help

lesleyb 11-12-2011 04:07 PM

You are retrieving the components of the line as a list construct. See http://perldoc.perl.org/perldata.html for more info on lists.

Also see http://perldoc.perl.org/perlvar.html for a better understanding of $_.



You really need to use one hash to contain all the elements of a customer's record.
And then build an array of hashes like this :

Code:

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

my @CustomerRecords; 
open(PH, "customers.txt") or die "Cannot open customers.txt: $!\n";
while(<PH>) {
    chomp;
    my %CustomerRecord;
    ($name, $number, $email) = (split(/s+/, $_));
    ## TODO:  split $name on the ',' character and re-order it
    ## before assigning to the 'Name' element of the temporary hash
    $CustomerRecord{'Name'} = $name;
    $CustomerRecord{'Number'} = $number;
    $CustomerRecord{'Email'} = $email;
    push @CustomerRecords, { %CustomerRecord };
}
close(PH);

At this point, $CustomerRecords[0]{'Name'} will contain the first John Smith and $CustomerRecords[6]{'Name'}
the second occurrence of John Smith.

See http://perldoc.perl.org/perldsc.html which discusses generating arrays of hashes.

Each element of your final array will be a hash (aka an associative array) containing one person's details.
Then rework the remainder of your code to use these data structures.

Good luck :)

bigearsbilly 11-14-2011 03:09 AM

BTW.

Code:

split(/s+/, $_
Is not how one does split on whitespace. Even though it is correct.
(It requires more typing, most un-perl)
Code:

split
is enough


split by default splits $_ on whitespace.
And furthermore, to split on whitespace you only need to do
split " " as this is a special case in perl.

split

SecretCode 11-14-2011 02:11 PM

Quote:

Originally Posted by jmc1987 (Post 4522300)
Actually I got it to work last night with this
...

What does your code return when you give it the name Smith,John? There are two Smith,John customers and it should return both of them!

jmc1987 11-25-2011 10:00 PM

Quote:

Originally Posted by SecretCode (Post 4523927)
What does your code return when you give it the name Smith,John? There are two Smith,John customers and it should return both of them!

Okay I see what your saying. I'm actually rewriting this code now but the keys is overwriting so technically I am losing hashes.

markush 11-26-2011 05:09 AM

Hi,

you are losing hashes if you don't create a new hash for every record! Otherwise you will always use the same hash and push the same hash with new data onto the array which results in an array with all entries the same.

The normal way to use an array of hashes in perl is to use references to hashes. This means you create a new hash for every record and push a reference to the new hash onto the array.

Markus

lesleyb 11-27-2011 06:31 AM

Quote:

Originally Posted by jmc1987 (Post 4534110)
Okay I see what your saying. I'm actually rewriting this code now but the keys is overwriting so technically I am losing hashes.

You're not 'losing' the hash, you are overwriting it.

The code I gave earlier declares a new hash for each record, from within the file reading loop. After all, you've one record per line and you need a hash for each record, yes? Plus you then need to push the hash
Code:

push @CustomerRecords, { %CustomerRecord };
, or its reference,
Code:

push @CustomerRecords, \%CustomerRecord ;
on to the array.
You could just use a reference within the loop,
Code:

while(<PH>) {
    chomp;
    my $CustomerRecord;
    ($name, $number, $email) = split;
    ## TODO:  split $name on the ',' character and re-order it
    ## before assigning to the 'Name' element of the temporary hash
    $CustomerRecord->{'Name'} = $name;
    $CustomerRecord->{'Number'} = $number;
    $CustomerRecord->{'Email'} = $email;
    push @CustomerRecords, { $CustomerRecord };
}

But your accessors will change to
Code:

$CustomerRecords[0]->{'Name'}
If you encapsulate this effort into a subroutine and return a reference to the array you have generated, now containing a list of hashes. then the accessor will change to
Code:

$CustomerRecords->[0]->{'Name'}

Sergei Steshenko 11-27-2011 02:31 PM

Quote:

Originally Posted by lesleyb (Post 4535124)
You're not 'losing' the hash, you are overwriting it.
...

The OP has already started a new thread about scope (in Perl).

jmc1987 11-28-2011 01:31 AM

Quote:

Originally Posted by Sergei Steshenko (Post 4535489)
The OP has already started a new thread about scope (in Perl).

Yea sorry lost this thread but the new thread is here

http://www.linuxquestions.org/questi...41#post4535941

Can a moderator perhaps lock this thread so all new post go to the other?

crabboy 11-29-2011 09:34 PM

done


All times are GMT -5. The time now is 09:09 PM.