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

Hello monks!

I'm still a Perl newb, but am having lots of fun learning. I've created a few projects for myself, just as exercises, but I find myself stuck at the following one.

Basically, the script will accept two commands: "!read" and "!write", with proper arguments, and get or set the email address for a specific username (check out the code comments for details). It will read the information from (or save it to) an external file.
dbhandler is the subroutine that I'd like to handle the actual reading and writing.

Anyway, the code below is as far as I've gotten. I'm really stuck. Among other things, I'm not too sure what the most efficient way is to scan the file for the correct entry, nor how to update an existing entry. Oh yes, and commands must be case-insensitive, so "!READ John" and "!read john" should be the same.

#!/usr/bin/perl -w # "!read username" will read username's e-mail address. # "!write username user@example.com" will set username's e-mail addres +s # to user@example.com. # # Commands must be handled case-insensitively. use strict; print ("Hello there, what would you like to do? "); my $input_command = <STDIN>; chomp ($input_command); # Break $input_command into a space-delimited array and assign only th +e # first element (which will be the command) to $command. my $command = (split /\s/, $input_command)[0]; $command =~ tr/A-Z/a-z/; # lowercase everything # Take the second element (the name) and assign it to $name my $name = (split /\s/, $input_command)[1]; # Take the third element and assign it to $address my $address = (split /\s/, $input_command)[2]; if ($command =~ /!read\b/ || $command =~ /!write\b/) { print ("The command you entered is $command\n"); if ($name) { print ("The name you entered is $name\n"); } if ($address) { print ("The address you entered is $address\n"); } dbhandler($command, $name, $address); } else { print ("Sorry, only !read and !write are valid commands.\n"); } sub dbhandler { my ($subcommand, $subname, $subaddress) = @_; # lowercase everything. $subcommand is already received lowercase $subname =~ tr/A-Z/a-z/; $subaddress =~ tr/A-Z/a-z/; if ($subcommand =~ /!read\b/ { open ADDYFILE, "addyfile" || die "Couldn't open file: $!"; # rest of the subroutine should go here

Any hints, advice (and code! :)) would be appreciated.
Thanks!

Replies are listed 'Best First'.
Re: Read from and write to file
by wojtyk (Friar) on Apr 05, 2007 at 20:09 UTC
    Well one suggestion would be using split efficiently...there's no need to call it three times.
    my ($command, $name, $address) = split(/\s/, $input_command);
    does the same thing as those 3 lines. Also, another option to lowercasing everything is simply to use a case-insensitive regex option (/i).

    As far as your specific question about scanning the file...I assume you'd do it the same way you're breaking down the input command...read line from file, split line, compare.

    I would say use Tie::File for max efficiency to make the change on the fly.

      Thanks very much for the suggestions, wojtyk.

      You're totally right about using split more efficiently. As for Tie::File, I'll be looking into that. I'm not too sure about using the \i option for regular expressions, as I want to ensure that the information entered gets saved as all lowercase, even if entered as e.g., "John" or "JOHN".

      Thanks again! :-)

        That is what the lc command is for

        TStanley
        --------
        War is an ugly thing, but not the ugliest of things. The decayed and degraded state of moral and patriotic feeling which thinks that “nothing is worth war” is much worse. The person who has nothing for which he is willing to fight, nothing which is more important than his own personal safety, is a miserable creature and has no chance of being free unless made and kept so by the exertions of better men than himself. -- John Stuart Mill
Re: Read from and write to file
by jethro (Monsignor) on Apr 05, 2007 at 22:40 UTC
    You might use the function lc to lowercase the whole string (i.e. $input_command) before you split it.

    You can read in a file line by line with  $line=<ADDYFILE> or in one single swoop with @file=<ADDYFILE>.

    To go through @file you could use foreach:
    foreach my $line ( @file ) { if ($line=m/^\s*$subname\s*:\s*(\S*)/i) { do something, we found it } }
    I'm assuming here that the format of @file looks something like this:
    submane1: address1 subname2 : address2 ...
    and addresses have no spaces in them.
      Ummm, shouldn't that be "=~" instead of "=" in the example above? As it stands, it'll always match the if condition, as you're assigning the value of the pattern match to the variable $line. *grin*