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

Hi How do we match a range of number which is being input by user? Something searching a range of X(0-22)Y(35-50). Is this possible to use /x[ 0-22 ] y [ 35-50]/??

Replies are listed 'Best First'.
Re: Match range of number
by johngg (Canon) on Nov 15, 2007 at 11:36 UTC
    You can't do ranges in regexen like that, it just sets up a character class containing 0, 1, 2 and 2. You could set up both ranges in arrays outside the regex and then use the pipe '|' alternation metacharacter.

    use strict; use warnings; my @toTest = qw{ X-1Y40 X13Y39 X20Y60 X22Y35 }; my $rxValid = do { local $" = q{|}; my @xRange = ( 0 .. 22 ); my @yRange = ( 35 .. 50 ); qr{(?x) ^ X (?:@xRange) Y (?:@yRange) $}; }; foreach my $test ( @toTest ) { print qq{$test: }, $test =~ $rxValid ? qq{valid\n} : qq{invalid\n}; }

    This produces the following.

    X-1Y40: invalid X13Y39: valid X20Y60: invalid X22Y35: valid

    I hope this is helpful.

    Cheers,

    JohnGG

      Regexp::List should create a very efficient regex in this case.
      use Regexp::List qw( ); my $rxValid = do { my $re_builder = Regexp::List->new(); my $x_range = $re_builder->list2re(0..22); my $y_range = $re_builder->list2re(35..50); qr/ ^ X $x_range Y $y_range $/x; };
        I'd say Regexp::List is surprisingly fast:
        perl bm.pl Rate rxValid plain Regexp::List rxValid 245760/s -- -19% -19% plain 303675/s 24% -- 0% Regexp::List 303675/s 24% 0% --
        --
        Andreas
Re: Match range of number
by andreas1234567 (Vicar) on Nov 15, 2007 at 11:34 UTC
    I might be wrong but I don't think you can. The range operator - applies to ranges of characters (including numbers), not a range of numbers (unless the numbers are below 10). See perlre.
    use strict; use warnings; while (my $line = <DATA>) { my $match = 0; chomp $line; if ($line =~ m/^X\((\d+)\)Y\((\d+)\)$/) { $match = 1 if ( $1 >= 0 && $1 <= 35 && $2 >= 22 && $2 <= 50); } ($match) ? print $line . q{ match} : print $line . q{ miss}; } __DATA__ X(0)Y(35) X(21)Y(49) X(55)Y(55) X(-1)Y(33)
    $ perl -l 650946.pl X(0)Y(35) match X(21)Y(49) match X(55)Y(55) miss X(-1)Y(33) miss
    --
    Andreas
Re: Match range of number
by moritz (Cardinal) on Nov 15, 2007 at 11:52 UTC
    my ($min, $max) = (35, 50); if ( m/^(\d+)(??{ $^N >= $min && $^N <= $max ? '' : '(?!)'})$/ ){ print "$_ is between $min and $max\n"; }

    This uses code assertions, see perlre for details.

    Note that this example only works for integers (otherwise you'd have to adapt the regex, preferably with Regexp::Common.)

      I don't see a reason to invoke the regex compiler for every match.

      if ( m/^(\d+)(?(?{ $^N < $min || $^N > $max })(?!))$/ ){ print "$_ is between $min and $max\n"; }

      $^N was introduced in 5.8. Using $1 would provide backwards compatibility if required.

        $^N was introduced in 5.8. Using $1 would provide backwards compatibility.

        ... at the cost of generality. If you want to use such a snippet multiple times in a regex you have to keep track which capture to use.

        Actually I'm working on a module that generates regexes for matching integers, and that's one of the fallback solutions. When you don't know how your regex will be used, you can't rely on the number of the capture.