Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

No <STDIN> yeilds username and password

by trenchwar (Beadle)
on Feb 08, 2008 at 22:37 UTC ( [id://667091]=perlquestion: print w/replies, xml ) Need Help??

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

So the script works perfectly...however in the last while loop if you enter a wrong name it gives the correct results, but if you enter nothing and hit enter it yields the username and password you have entered previously. Long story short, I am trying to achieve the result "I'm sorry, there is no one here by that name, please try another name or type exit to end" when a user hits enter.
#!/usr/bin/perl print "Type 'exit' at any time to end this program\n"; while(1) { print "Please enter your first name\n"; chomp ($fname =<STDIN>); exit if $fname eq 'exit'; { last; } print "Please enter your last name\n"; chomp ($lname =<STDIN>); exit if $lname eq 'exit'; { last; } print "Please enter a password\n"; chomp ($password =<STDIN>); exit if $password eq 'exit'; { last; } print "Please enter 'done' or hit enter.\n"; chomp ($ending =<STDIN>); exit if $ending eq 'exit'; if ($ending eq ' ' or 'done') { last; } } my $username = "$lname, $fname"; %name_hash; $name_hash{$username} = $password; print "To search for a user please type in a name and hit enter.\n"; while(<STDIN>) { chomp; exit if $_ eq 'exit'; foreach my $key (keys %name_hash) { if ($key =~ m/$_/i) { print "The username is $username and the passwo +rd is $password, please enter another name to search\n"; } else { print "I'm sorry, there is no one here by that +name, please try another name or type exit to end.\n"; } } }

Replies are listed 'Best First'.
Re: No <STDIN> yeilds username and password
by moritz (Cardinal) on Feb 08, 2008 at 22:53 UTC
    The problem is that you have to loop over the whole hash to know that a username wasn't found. You could do it like that:
    my $found = 0 foreach my $key (keys %name_hash) { if ($key =~ m/$_/i) { print "The username is $username and the password is $p +assword, please enter another name to search\n"; $found = 1; } } if (!$found) { print "I'm sorry, there is no one here by that name, please try an +other name or type exit to end.\n" }

    (there are better ways to achieve case insensitivity, but it's a start).

    Another hint: your code will only store the last username and password that was entered. Do you know why?

Re: No <STDIN> yeilds username and password
by Cody Pendant (Prior) on Feb 09, 2008 at 05:07 UTC
    Not that there's anything wrong with the above answers, but surely the reason for the specific problem:
    if you enter nothing and hit enter it yields the username and password you have entered previously
    Is that if you do that, you're doing the regex with an empty string. Which matches.

    You're essentially doing if ($key =~ m//i) if they just hit return.

    So you need another if clause as well as the one for "if it's 'exit'" which says what to do if it's empty.



    Nobody says perl looks like line-noise any more
    kids today don't know what line-noise IS ...
      Hey Cody....your exactly right.... I am trying to figure out the clause that will loops it back to the beginning of the while statement if it is empty.
Re: No <STDIN> yeilds username and password
by almut (Canon) on Feb 09, 2008 at 00:08 UTC

    If case-insensitive lookup (and storage) of the usernames is the idea, then you could apply the same case transformation on the hash key (all uppercase, all lowercase, etc.) prior to storing the entry, and then similarly upon lookup, e.g.

    ... $name_hash{uc $username} = $password; ... while(<STDIN>) { chomp; exit if $_ eq 'exit'; my $name = uc $_; if (exists $name_hash{$name}) { print "The password for $name is $name_hash{$name}, please ent +er another name to search\n"; } else { print "I'm sorry, there is no one here by that name, please tr +y another name or type exit to end.\n"; } }

    This would simplify things by taking advantage of hashes' key feature (pun intended) to allow a direct lookup, without having to loop over all of its entries.

    Note that this suggestion differs slightly from what your code does, in that yours will (a) find the name even if only a substring of it has been specified as the lookup string, and (b) will return multiple matches if the hash happens to be storing several differently spelled variants (Name, name, NAME, ...) of the same username...  I'm not sure whether you actually need those features, so I'm mentioning this simplification just in case my suspicion should hold true that you don't... :)

Re: No <STDIN> yeilds username and password
by eric256 (Parson) on Feb 10, 2008 at 21:32 UTC

    The first thing I did was add strict and warnings. This pointed out several instances where variables weren't declared using my. So I added my in there. A nice little shortcut is that you can actually us it in your chomp (my $fname =<STDIN>); statement. See how I put my in there? Now $fname is declared in that block. I did that for all your input statements.

    Then I noticed all of your last blocks, I don't know what you thought they where doing, but as far as i can tell they simple exited the block that they where in (which didn't do anything because last was the only thing in that block. At this point the entire top section of your code worked but now I got errors when you where inserting into the hash. At this point it finally donned on my why you had a loop at the top, I am assuming that you wanted to allow a person to add several users? So moving that assignment up into the loop and re-arranging your exit condition got me that (lines 26-31 in the code below).

    Now I started on your actual question as posted here and looked at the lower block. I noticed that lines 50-52 would print for every user that didn't match your criteria, and that didn't seem right, so I moved them out of the loop and added a $found variable to keep track of how many users actually matched the search criteria you provided. Now the search was working good, but searching for a blank line, space, or comma would return every user that was entered (since they would all have a comma and space between the names.) So right after your line 45 I added next unless $_ =~ /\w/; This says to go back to the top of the loop (next) unless the search criteria $_ contained at least one word character (\w which is 0-9, a-z and A-Z).

    A few more cosmetic changes, and some verbiage changes and you get the code below

    #!/usr/bin/perl # always use strict and warnings use strict; use warnings; #hash to store names my %name_hash; print "Type 'exit' at any time to end this program\n"; #loop to add new users while(1) { print "Please enter your first name\n"; chomp (my $fname =<STDIN>); exit if $fname eq 'exit'; print "Please enter your last name\n"; chomp (my $lname =<STDIN>); exit if $lname eq 'exit'; print "Please enter a password\n"; chomp (my $password =<STDIN>); exit if $password eq 'exit'; my $username = "$lname, $fname"; $name_hash{$username} = $password; print "Please enter 'done' to start seachings or hit enter to a +dd another user.\n"; chomp (my $ending =<STDIN>); exit if $ending eq 'exit'; last if $ending eq 'done'; } print "To search for a user please type in a name and hit enter.\n"; while(<STDIN>) { chomp; exit if $_ eq 'exit'; next unless $_ =~ /\w/; #require at least one letter in the qu +ery my $found = 0; foreach my $key (keys %name_hash) { # guard the regex with \Q so that users can't entere th +eir own regex if ($key =~ m/\Q$_\E/i) { print "The username is '$key' and the password +is '" . $name_hash{$key} . "'\n"; $found++; } } if ($found) { print "I found $found users matching that search. "; } else { print "Sorry no users where found matching '$_'. "; } print "Please try another name to search or type exit to end\n" +; }

    I believe this performs closer to your desire. The name input is still kinda of awkward but thats a minor fix. I know that everyone learns different and sometimes it helps to have someone else fix the problem their way, now you can go back and make whichever changes to your original code while having a solutions that works (hopefully as you expected) ;). Welcome to perl and PerlMonks and enjoy!


    ___________
    Eric Hodges
Re: No <STDIN> yeilds username and password
by apl (Monsignor) on Feb 09, 2008 at 13:30 UTC
    I'm curious... Did you try using the debugger or print statements? Did you do any research before posting the question?
      Nah...I just figured eff it! kidding.

      I did what I could, but I am clearly a novice trying to learn something new.

      It's like anything else in life, you can't expect to know how to ride a bike, speak another language or succeed without the help and encouragement of others.

      If I truly knew the answer to my question or how to fix it I wouldn't be here humbly seeking the knowledge of others.

      And when others on here berate and belittle those who try to learn it makes me and other want to seek knowledge elsewhere.

      I assumed Perlmonks was a place to go for the help of the best of the best, people who understood and wanted to help those who don't.

        I asked because of Should a Socratic Dialogue be attempted?. I'm still not certain whether it's better to just provide an answer, or to explain to a questioner how to go about solving the problem by their own efforts. My question of you was driven by that uncertainty. It wasn't my intent to be insulting, and I regret that was how I came across.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2024-04-26 07:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found