http://www.perlmonks.org?node_id=395385

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

Monks,

My apologies if this has already been answered. I've searched using Super Search, but I don't think I'm giving it what it wants.

What I thought would turn out to be a fairly easy match, has got me pulling my hair out. As you can see from the below, I'm trying to match any address between 192.168.1.x - 192.168.99.x, but the only three it will match is:
192.168.1.50 192.168.6.50 192.168.8.50
Even if I change the range to 20-99.
use warnings; use strict; while (<DATA>){ print "$_\n" if /192\.168\.[1-99]\.\d+/g; } __DATA__ 192.168.1.50 192.168.6.50 192.168.8.50 192.168.28.50 192.168.58.50 192.168.101.50 192.168.201.50
Thanks,
Dru

Replies are listed 'Best First'.
Re: Match a Range of IP's
by conrad (Beadle) on Sep 30, 2004 at 16:15 UTC
    [1-99] is a character range, not a number range, so it'll match 1 .. 9 or 9 (i.e. it's the same as [1-9]).

    One solution is to use [1-9]\d? instead, which will match 1 .. 9 and any two-digit number (but if you ever get IP addresses of the form "001" this will fail).

    A better option would be to use (\d+) and add " && $1 >= 1 && $1 <= 99" to the end of the line; bear in mind that that will run into trouble if you have multiple IP addresses per line because of your /g though...

Re: Match a Range of IP's
by bpphillips (Friar) on Sep 30, 2004 at 16:13 UTC
    your [1-99] really means match only one character [1-9] (you can't do ranges on two-digit numbers). You need this:
    while (<DATA>){ print "$_\n" if m/^192\.168\.[1-9][0-9]?\.\d+$/; }
Re: Match a Range of IP's
by Zaxo (Archbishop) on Sep 30, 2004 at 16:21 UTC

    The [1-99] doesn't do what you think. It is a character class which will match a single digit in the range one to nine. That corresponds to what you see.

    You can match and test with

    while (<DATA>) { print if /^192\.168\.(\d{1,3})\.(\d{1,3})\b/ and $1 > 0 and $1 < 100 and $2 < 256; }
    I like to do this kind of thing with IP numbers from Socket::inet_aton(), using bitwise ops, but that isn't such an advantage where netmasks don't correspond to the ranges.

    After Compline,
    Zaxo

Re: Match a Range of IP's
by DrHyde (Prior) on Sep 30, 2004 at 16:56 UTC
    Check out Net::CIDR. You want the range2cidr and cidrlookup functions.
Re: Match a Range of IP's
by SpanishInquisition (Pilgrim) on Sep 30, 2004 at 17:26 UTC

    Regexp::Common::net looks good. See if it's valid and then check the individual octets once you parse it.

Re: Match a Range of IP's
by runrig (Abbot) on Sep 30, 2004 at 18:13 UTC
    If I understand correctly, you just want:
    use Socket qw(inet_aton); my $lower = inet_aton("192.168.1.0"); my $upper = inet_aton("192.168.99.255"); while (<DATA>) { chomp; my $ip = inet_aton($_); print "$_\n" if $lower le $ip and $ip le $upper; }
Re: Match a Range of IP's
by lhoward (Vicar) on Sep 30, 2004 at 18:22 UTC
    IMHO, the right way to do this is to convert all the IPs to integers and compare those. Comparing with a RE will work for most common ones, but trying to determine if 144.7.14.2 is betwen 9.0.1.244 and 199.121.5.19 is not well suited for a regular expression.
    if((ip2int($iplow)<=ip2int($ip))\ &&(ip2int($ip) <=ip2int($iphigh))){ #... } sub ip2int{ my $ip=shift; my @s=split /\./,$ip return $s[3]+($s[2]+($s[1]+$s[0]*256)*256)*256; }
Re: Match a Range of IP's
by si_lence (Deacon) on Sep 30, 2004 at 16:18 UTC
    Hello

    You are using a character class in your match.
    [1-99] means a "1" to "9" and excatly one of it.

    To select only one or two digit number use somethin like
    print "$_\n" if /192\.168\.\d\d?\.\d+/g
    si_lence Update: bpphillips is right with his answer (shame on me!), so I changed the description.
    At least the regex was ok ;-)
      actually, within a character class, a "-" character indicates a range unless it's at the beginning or the end of the character class
Re: Match a Range of IP's
by fokat (Deacon) on Sep 30, 2004 at 23:18 UTC

    Hi there:

    This is a task for NetAddr::IP...

    perl -MNetAddr::IP -ne 'print "$_ match\n" if NetAddr::IP->new("192.168.1/24")->contains(NetAddr::IP->new($_))'

    Best regards

    -lem, but some call me fokat

      NetAddr::IP->new("192.168.1/24")->contains(NetAddr::IP->new($_))
      Except that the CIDR address you have doesn't match the OP's desired range (192.168.1.0-192.168.99.255), so he'd have to run your code up to 99 times to see if the address is in that range. Or just another way (not tested):
      use Net::CIDR::Lite; my $range = Net::CIDR::Lite->new("192.168.1.0-192.168.99.255"); while (<DATA>) { chomp; print "$_\n" if $range->find($_); }
Re: Match a Range of IP's
by Dru (Hermit) on Sep 30, 2004 at 18:02 UTC

    Thank you everyone. Alot of good suggestions. I'll have to check each one of them out.
Re: Match a Range of IP's
by TedPride (Priest) on Oct 01, 2004 at 09:42 UTC
    while (<DATA>) { print if /^192.168.[1-9]\d?.([1-9]\d?\d?)$/ && $1 < 256; }
    1-99 is either one or two digits, with the first digit always being 1 or greater. Then you have to check for a valid 4th pair, which means 1-3 digits with the first being at least 1 and the whole thing being less than 256.