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

I have written a program that will intake a bunch of information from a user, and based on this will search some files for this information. I'm trying to make the code more robust, and be able to handle more errors. One error that I would like to fix, is if the user happens to input the wrong type of information. For instance, the user hits a letter when the code wants a number. I know perl will take the which ever input you give it, but I need to be able to differentiate between the two in order to perform other checks using comparisons. Any help would be appreicated, below is a sipit of my code that hopefully shows the issue a little.
print "\nPlease enter the start date for your search (mm/dd/yyyy hh:mm +): "; $date = <>; chomp( $date ); @eDate = split( /\/| |:/, $date ); $sMonth = $eDate[0]; $sDay = $eDate[1]; $sYear = $eDate[2]; $sHour = $eDate[3]; $sMinute = $eDate[4]; #Verify the user enter all neccessary information if( defined( $sMonth ) == 0 || defined( $sDay ) == 0 || define +d( $sYear ) == 0 || defined( $sHour ) == 0 || defined( $sMinute ) == +0) { $skip = 1; print "Please enter all required data fields.\n"; } #Verify the month entered by the user. if( $skip == 0 && $sMonth > 0 && $sMonth < 13 ) { $correct = 1; }
The second if block is where the incorrect data type error is being thrown.

Replies are listed 'Best First'.
Re: catching incorrect input type
by moritz (Cardinal) on Apr 02, 2012 at 15:03 UTC
Re: catching incorrect input type
by Marshall (Canon) on Apr 02, 2012 at 15:33 UTC
    A general command line loop can be done like this:
    The while() condition issues the prompt and gets what the user says. Then we proceed with more detailed analysis.
    - General rules for command input: spaces - leading - trailing don't matter - blank lines don't matter.
    - Use a regex to see if all the stuff is there- here if $minute is not defined, that means the regex didn't match - just say: bad format and prompt with the formatting hint - the user will stare at it and figure it out.
    -Then start checking each individual thing for a range of "correctness" as far as you can do for one field at a time. Issue error on the first error seen and re-prompt.

    Checking user input can get pretty "wordy" - this is one area where the Perl code won't be much shorter than the C code. Don't worry about that. Be logical and straight-forward. Remember that you are parsing something that a person is typing - super efficiency is not required - logical thinking and clarity of the error messages is!

    If this winds up being a page of code - don't worry about it - again you are running maybe 10 if statements - speed is not an issue here - clarity is an issue.

    #!/usr/bin/perl -w use strict; my $prompt = "Enter the start date (mm/dd/yyyy hh:mm): "; my $line; while ( (print "$prompt"), $line =<STDIN>) { next if $line =~ /^\s*$/; #simple reprompt on blank line my ($month, $day, $year, $hour, $minute) = $line =~ m|^\s*(\d+)/(\d+)/(\d+)\s+(\d+):(\d+)\s*$|; if (!defined $minute) #regex failed initial "laugh test" { print "illegal format!\n"; next; } if ($month <1 or $month>12) { print "got month of $month - must be 1-12\n"; next; } if ($day <1 or $day>31) { print "got day of $day - must be 1-31\n"; next; } # more conditions here #finally try to convert the date to an epoch and see #if that works, eg 2/31/2012 should fail... #all tests pass, you have a "good date time" } __END__ C:\TEMP>commandloop5.pl Enter the start date (mm/dd/yyyy hh:mm): fjfjf 97609876 afadfa illegal format! Enter the start date (mm/dd/yyyy hh:mm): 0/0/2012 99:00 got month of 0 - must be 1-12 Enter the start date (mm/dd/yyyy hh:mm): 1/0/2012 99:00 got day of 0 - must be 1-31 Enter the start date (mm/dd/yyyy hh:mm): I stopped program
    I would drop these words like "Please" in the prompt - get to the point - don't be overly polite.
    You can use elsif clauses but don't overly worry about efficiency - the whole loop will run much, much faster than the user can even type one character - literally the "enter" key is still moving upwards even after the whole user input has already been validated.

    Update: Note that the initial regex is fairly loose:
    digit(s)/digit(s)/digit(s)        digit(s):digit(s)
    is "close enough" - get the basic syntax in the first regex
    apr/2/2012 00:02 is not "close enough".
    Once the basic syntax requirements are met, then get into the details of what these various fields mean and what the valid values are for them. I am assuming that you want to be "reasonably" user friendly - and not just "screw you - this isn't exactly what I asked for".

      Not really the way to check a date, it's already a lot of code and still far from being correct. Use appropriate module:

      use 5.010; use strict; use warnings; use DateTime::Format::Strptime; my $strp = DateTime::Format::Strptime->new( pattern => '%m/%d/%Y %H:%M +', ); while (<DATA>) { chomp; my $dt = $strp->parse_datetime($_); if ($dt) { say "Parsed: $dt"; } else { say "Invalid date: $_"; } } __DATA__ 2/21/2012 fjfjf 97609876 afadfa 0/0/2012 99:00 1/0/2012 99:00 02/31/2012 01:55 02/21/2012 01:55
Re: catching incorrect input type
by BillKSmith (Monsignor) on Apr 02, 2012 at 22:56 UTC
    Use a prompt module to get user input. The module should notify the user of his errors and reprompt. Yes, you have to provide a validation routine but it does not have to anything but return a boolean indicating whether or not the the entry is valid. "Valid" should mean not only that the data is in the correct format, but also that it is within a reasonable range. ActiveState::Promt is very easy to use on windows. Similiar capablities are available for other platforms.