سؤال

I'm having difficulty validating user input with my regex. I'm trying to take user input and make sure it's between 1 and 99. If it's not then it calls the same sub-routine until a validating entry is matched. However, even if the input is between 1-99 it will keep calling the sub routine.

sub get_age
{
    print "Age :";
    @get_age = <STDIN>;
    if (scalar @get_age  !~ m/\d[1-99]/ ) #making sure age given is between 1 and 99
    {

            print "ERROR, invalid input\n";
            &get_age; #this is callng the same sub it's in to re-run validating
    }

            push(@get_age, <STDIN>); #taking name from user and putting it in @get_name     array
            chomp(@get_age); #erasing newline from input
}
هل كانت مفيدة؟

المحلول

You seem to be confused about

  1. What the @ sigil does in Perl,
  2. How character classes in regexes work,
  3. What context in Perl is
  4. How the readline operator <...> behaves depending on context.
  5. (Why you should never do &foo).

The @ sigil

The @ denotes an array. This imposes list context on the right hand side of an assignment.

Character classes

A character class is denoted by square brackets, and can include ranges of characters, e.g. [0-9] — match the arabic digits. The \d named character class matches many more characters — whatever Unicode considers "numeric".

A character class can be negated with a leading caret. E.g. [^0-9] matches any character that is not an arabic digit.

Context

Perl operators behave differently depending on context. For example the readline operator:

  • In scalar context (singular), the readline operator reads a single line, but
  • in list context (plural), the readline operator reads all lines until the end of file (EOF) is encountered. To send the EOF on keyboard input, you usually use Ctrl-D.

When an @array is encountered, it evaluates to the list of all elements, in list context. However, in scalar context, it evaluates to the length of the array.

What @get_age = <STDIN> does

Because the LHS is an array, the RHS is evaluated in list context. As explained above, this reads input until the stream is exhausted. Each element in @get_age holds one line of the input after the assignment.

What scalar @get_age !~ m/\d[1-99]/ does

This expression is similar to

my $length = scalar @get_age;
not $length =~ /\d[1-9]/;

I.e. the array in scalar context evaluates to the length of the array, which is the number of lines in the input.

The regex says “Inside the string, there must be one unicode numeric character followed by either 1, 2, 3, 4, 5, 6, 7, 8, or 9”. Multiple occurrences of a character in a charclass are discarded. The string may include arbitrary other characters. This should match: "my age is 020!"

What you probably wanted to do:

sub get_age {
  print "Age: ";
  my $age = <STDIN>; # scalar context, and put into a private variable
  chomp $age;        # remove newline
  if( not length $age ) { # check for empty string
    say "You didn't type anything. Retry!";
    return get_age();
  }
  if( $age =~ /[^0-9]/ ) { # recurse on any non-digits
    say "You typed non-numeric characters. Restrict yourself to '0' through '9'. Retry!";
    return get_age();
  }
  unless( 1 <= $age && $age <= 99 ) { # check for correct bounds
    say "Valid ages have to be in the range 1 to 99. Retry!";
    return get_age();
  }
  return $age;
}

Then in your main code:

my $age = get_age();

نصائح أخرى

#!/usr/bin/env perl

use 5.012;
use strict; use warnings;
use Term::Prompt qw( prompt );

my $age = prompt(s => 'Age:', 'between 1 and 99', '', sub {
        my ($input) = @_;
        ($input) = ($input =~ /\A\s*( [1-9] [0-9]? )\s*\z/x);
        return $input and ($input >= 1) and ($input <= 99);
    }
);

say $age;

[1-99] is the set of all characters from 1 to 9 and the character 9.

Try [1-9]|([1-9]\d) instead. That means either 1-9 or any two numbers not starting with a zero.

I think this is an improved version of your code:

use strict;
use warnings;

sub get_age_and_name
{
    my $age;
    while(1) {
        print "Age : ";
        $age = <STDIN>;
        chomp $age;
        # Making sure age given is between 1 and 99
        last if( $age =~ m/^\d\d?$/ and ($age>=1) and($age<=99) );
        print "Error ERROR, invalid input\n";
    } 

    print "Name : ";
    my $name = <STDIN>;
    chomp $name;
    return ($age, $name);
}


my ($age, $name) = get_age_and_name;
print "($age, $name)\n";
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top