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

Hello Monks, I'm working on a script that requires several user inputs. While attempting to validate the input, I've come accross a problem. I've successfully written validations for all but one of those inputs. I'm asking for a Customer Abbreviation and then check to make sure the input is either 3 or 4 characters long (can be letters and/or numbers). Here is what I came up with:
print "Customer abbreviation: "; $cust = <STDIN>; print "\n"; chomp ($cust); until($cust =~ <\w{3,4}>) { print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; print "!!! !!!\n"; print "!!! ABBREVIATION MUST BE 3 OR 4 CHARACTERS !!!\n"; print "!!! !!!\n"; print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; print "\n"; print "Customer abbreviation: "; $cust = <STDIN>; chomp ($cust); print "\n"; }
for some reason, if a valid pattern is entered, it still asks to re-enter three times. Also, if I continue to enter an invalid pattern (2 or 5 characters), after looping twice it will continue to ask for the remainder of the inputs, as shown here:
Customer abbreviation: AB !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! !!! !!! ABBREVIATION MUST BE 3 OR 4 CHARACTERS !!! !!! !!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Customer abbreviation: AB !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! !!! !!! ABBREVIATION MUST BE 3 OR 4 CHARACTERS !!! !!! !!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Customer abbreviation: AB BAN Number:
Any and all wisdom is greatly appreciated.

Replies are listed 'Best First'.
Re: problem with pattern match
by kennethk (Abbot) on Jun 11, 2010 at 18:29 UTC
    I'm not quite sure what is actually implemented there, but the issue appears to be in:

    until($cust =~ <\w{3,4}>) {

    You should get your expected behavior by changing that to:

    until($cust =~ /\w{3,4}/) {

    since you are trying to match against a regular expression. I'd also add start and end anchors, as your expression as written would also match a 17-letter expression - it contains multiple 3 and 4 letter sets.

    until($cust =~ /^\w{3,4}$/) {

    As a side note, you can use last to do flow control and reduce your code repetition, as well as use a heredoc:

    use strict; use warnings; while (1) { print "Customer abbreviation: "; my $cust = <STDIN>; print "\n"; chomp ($cust); last if $cust =~ /\w{3,4}/; print <<EOT; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!! !!! !!! ABBREVIATION MUST BE 3 OR 4 CHARACTERS !!! !!! !!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! EOT }
      I'm not quite sure what is actually implemented there...

      My guess is that the programmer's directory has 3 files starting with "www" in it. <\w{3,4}> in some shells will match a file that begins with www or wwww. The angle brackets would cause a directory glob, setting $_ to file names matching what's inside once per loop.

      On the other hand, according to "perldoc File::Glob", that pattern should match only files named "w3" or "w4". And even if the shell was interpreting it, it would only match files "www" or "wwww" with nothing following, since it doesn't end with a "*". So I'm a bit at a loss as well.

        Your guess would be wrong.    Whether or not there are matching file names the file glob operator will return the two strings 'w3' and 'w4'.

      If you want to use non standard delimters "/" you have to do it like this:

       m<PATTERN>

      otherwise you have to stick to :

       /PATTERN/

      Thank you, this worked perfectly.
Re: problem with pattern match
by toolic (Bishop) on Jun 11, 2010 at 19:16 UTC
    kennethk has provided a solution.

    When your code doesn't work as expected, one debug technique is to use B::Deparse to see what code you are actually running (Tip #6 from the Basic debugging checklist). This shows that those angle brackets are really File::Glob:

    perl -MO=Deparse 844275.pl print "Customer abbreviation:\t \t"; $cust = <STDIN>; print "\n"; chomp $cust; use File::Glob (); until ($cust =~ glob('w{3,4}')) { print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; print "!!! !!!\n"; print "!!! ABBREVIATION MUST BE 3 OR 4 CHARACTERS !!!\n"; print "!!! !!!\n"; print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"; print "\n"; print "Customer abbreviation:\t \t"; $cust = <STDIN>; chomp $cust; print "\n"; }