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

reading from file.

by scripter87 (Novice)
on Nov 13, 2013 at 20:14 UTC ( [id://1062456]=perlquestion: print w/replies, xml ) Need Help??

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

Hi guys I am trying to read names from a file for a login program. If I put one name in the file and try do it, it works but if I add extra names it dont.
my $name = "names.txt"; my $i = 0; my$t; my @names; my $input = <STDIN>; open(FILE, "<", "$names") or die "Cannot open $names: $!\n"; while ($t=<FILE>){ push (@names, $t); } while ($i<scalar(@names)) { if ($input ne $names[$i] ) { print "not valid"; } $i++; }
If any of you guys can see where I am going wrong let me know. thanks guys

Replies are listed 'Best First'.
Re: reading from file.
by kcott (Archbishop) on Nov 13, 2013 at 21:49 UTC

    G'day scripter87,

    You haven't really provided sufficient information. "it works" and "it dont" and spectacularly useless error reports. You haven't shown how you're testing this: i.e. what you're using for input. You haven't shown your data: there could be line ending and embedded whitespace issues. A better question gets better answers: take a look at the guidelines in "How do I post a question effectively?" to see how you can achieve this.

    There's issues with the code as posted. It will print "not valid" for every record in names.txt until it either finds a match or reaches EOF. I see Eily pointed out an issue with $names: you said this was a typo; if you copy and paste your actual code, this won't happen. Do you really want to open/read/close the entire file for every "my $input = <STDIN>;"? Do you allow more than one attempt?

    Here's a potentially better way to do what you want:

    #!/usr/bin/env perl use strict; use warnings; use autodie; my $name_file = 'names.txt'; open my $name_file_fh, '<', $name_file; chomp(my @names = <$name_file_fh>); close $name_file_fh; print 'Enter login name: '; chomp(my $input = <STDIN>); print "Invalid!\n" unless grep { $_ eq $input } @names;

    Note that you can wrap the 'Enter login name: ' part in a loop to allow multiple attempts without having to reread the file. Not shown in your posted code, so I haven't tried to make guesses here, but you'll need to code some sort of successful login actions.

    Update: Prompted by Eily's mention of a hash and exists, which I saw after posting, this would be an improvement on what I originally provided:

    #!/usr/bin/env perl use strict; use warnings; use autodie; my $name_file = 'names.txt'; open my $name_file_fh, '<', $name_file; my %name = map { chomp; $_ => 1 } <$name_file_fh>; close $name_file_fh; print 'Enter login name: '; chomp(my $input = <STDIN>); print "Invalid!\n" unless exists $name{$input};

    -- Ken

      Unless the script is long running the hash solution is still sub optimal. Read the name, then look through the file, will on average read half the file for a valid name. The hash solution must always read the entire file. A better way to store the file could improve things even more, but perhaps we get silly...

      Cheers,
      R.

      Pereant, qui ante nos nostra dixerunt!

        I noted there was insufficient information. I asked whether multiple attempts were allowed. I presented a clearly stated potential solution.

        Allowing multiple attempts is the norm. I raised the absence of code to achieve this (i.e. while loop) along with a lack of any handling of a successful login attempt.

        Both the array and hash solutions read the entire file; obviously, this is not something specific to the hash solution. The point I made was that this was only done once regardless of the number of attempts.

        -- Ken

Re: reading from file.
by Eily (Monsignor) on Nov 13, 2013 at 20:28 UTC

    While running under strict (use strict; on the first line): Global symbol "$names" requires explicit package name at - line 8.. The scalar you declared is $name, with no s at the end.

    Just so you know, you can write the second loop as:

    for my $el (@names) { print "not valid" if $input ne $el; }

      sorry that was a typo. Can you see any particular reason that the file reads if there is only text on the first line of the file?
Re: reading from file.
by ramlight (Friar) on Nov 13, 2013 at 21:00 UTC
    It looks to me like you may have your logic backwards. For any list of with more than one name, it will always print "not valid". I suspect that you meant something like
    if input_name is in list: print "valid" else: print "not valid" This could be implemented with the following code: if (grep(/^$input$/, @names) { print "Valid\n"; } else { print "Not valid\n"; }
    There are a number of other areas that could be cleaned up. For example, you probably should read the list of valid names before getting your name to test. A little more consistency in your use of brackets would also make your code easier to figure out (both for you and for us.)
      thanks for the feedback. The basic thing I am trying to grasp is.. how to read files from a file for login purposes, so if I enter a name that is not in the file it obviously shouldnt work but as I said at the moment if I have just one line in the file it works ok but if more than one it doesnt.

        Although there are several solutions for your task, consider the following:

        use warnings; use strict; my $names = 'names.txt'; my ( $found, $name ); print 'Enter a name: '; chomp( my $input = <STDIN> ); open my $fh, '<', $names or die "Cannot open $names: $!\n"; while ( $name = <$fh> ) { chomp $name; if ( $input eq $name ) { $found++; last; } } close $fh; if ($found) { print qq{The name "$input" was found.\n}; } else { print qq{The name "$input" was not found.\n}; }

        Note that:

        • The name to search for is requested first.
        • chomp is used to remove the trailing input record separator (usually \n), from both the entered name and the names from the file.
        • The names' file is read once; no hash or array is necessary to 'hold' the names (Why process the list twice?). A flag's set and the while loop is immediately exited if the name is found.
        • Appropriate responses are given depending upon the value of $found.

        Hope this helps!

        You should have tried the solution ramlight gave you, it does look like what you were looking for. Maybe grep { $_ eq $input } @names instead of grep(/^$input$/, @names) would avoid some trouble with the metacharacters in $input though. And if you don't understang what it does, try and read grep's documentation :).

        Another way to do it would be using a hash, because you have exists to check if a name exists (obviously) in it.

        my @nameList = qw/Anna Beatrix Claude Damian/; my %names = map { $_ => 1 }, @nameList; # associate the value 1 to eac +h name in @namelist. Actually the value could be anything in this cas +e print "Anna exists" if exists $names{"Anna"}; print "Paul doesn't exist" unless exists $names{"Paul"};

        And as taint already stated, chomp your strings :)

        Thinking out loud;

        chomp ...
        foreach line as line ...

        Well, something like that.

        --Chris

        #!/usr/bin/perl -Tw
        use Perl::Always or die;
        my $perl_version = (5.12.5);
        print $perl_version;

Log In?
Username:
Password:

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

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

    No recent polls found