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

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

Hi, I've needed to validate user input to ensure it is a validly formatted IPv4 address.

The one I came up with before I looked around was:
/(^(\d{1,3}\.){3})\d{1,3}$/ The double brackets are to ensure the ^ stands for 1 triplet, not all three of them such as  (^\d{1,3}\.){3} which could never occur

Is there anything failsafe yet more efficient than this?

After hunting around I discovered suprisingly that many mail programs and even the LRP favour using this regex:
/^\d+\.\d+\.\d+\.\d+$/ Which will fail on things like 1111.0.0.0

As a side note, I'm sure there is a way to compare two variables to the same regex in one neat expression rather than
if ($var1 =~ /$regex/&&$var2 =~ /$regex/) I guess for an array a foreach can be used, but just for 2 variables only is there a way?

Thanks folks.

Replies are listed 'Best First'.
Re: IPv4 regex (Net::IPv4Addr)
by ybiC (Prior) on Feb 18, 2001 at 18:21 UTC
    Have you considered the Net::IPv4Addr CPAN module?   I've yet to use it myself, but it looks like it might fit your bill.

    From README:
    Net::IPv4Addr provides functions for parsing IPv4 addresses both in traditional address/netmask format and in the new CIDR format.   There are also methods for calculating the network and broadcast address and also to see check if a given address is in a specific network.

    And from POD:

    ipv4_checkip if ($ip = ipv4_checkip($str) ) { # Do something } Return the IPv4 address in the string or undef if the input doesn't co +ntains a valid IPv4 address.
        cheers,
        Don
        striving for Perl Adept
        (it's pronounced "why-bick")
      I got the module and this is how they do it:
      my $ip_rgx = "\\d+\\.\\d+\\.\\d+\\.\\d+"; sub ipv4_chkip($) { my ($ip) = $_[0] =~ /($ip_rgx)/o; return undef unless $ip; # Check that bytes are in range for (split /\./, $ip ) { return undef if $_ < 0 or $_ > 255; } return $ip; }
      Perfect, I'm not going to use the module for my application though because I wanted it to run off standard packaged modules so some totally inept computer users could make use of the script without my assitance. The code however may be making an appearance -- Thanks.

        Heh. I just wanted to note that the test for $_ < 0 is a waste since nothing matching \d+ can be negative.

                - tye (but my friends call me "Tye")
Re: IPv4 regex
by tadman (Prior) on Feb 19, 2001 at 05:08 UTC
    It's funny how people go to the ends of the Earth and back again to do something as simple as validate an IP address, and they don't even handle all possible valid addresses.
    use Socket; sub IsValidIP { return $_[0] =~ /^[\d\.]*$/ && inet_aton($_[0]); }
    This code uses only standard Perl packages.

    Again, N.N.N.N is not the only valid IP address format. Try visiting http://3625994804/ and you will see.
Re: IPv4 regex
by boo_radley (Parson) on Feb 18, 2001 at 17:54 UTC
    here's something off the top of my head.
    don't rush to trade compactness for readability or usefullness,btw.
    my $ip ="1.133.123.123"; if (IP_valid ($ip)) {print "$ip ok\n"} else {print "$ip bad\n"} my $ip =""; if (IP_valid ($ip)) {print "$ip ok\n"} else {print "$ip bad\n"} my $ip ="1asdf1.133.123.123"; if (IP_valid ($ip)) {print "$ip ok\n"} else {print "$ip bad\n"} my $ip ="12341.133."; if (IP_valid ($ip)) {print "$ip ok\n"} else {print "$ip bad\n"} my $ip ="not at all valid"; if (IP_valid ($ip)) {print "$ip ok\n"} else {print "$ip bad\n"} sub IP_valid { my $ip = shift; $ip =~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; foreach ($1,$2,$3,$4){ if ($_ <256 && $_ >0) {next;} return 0; } return 1; }
      Yes, I normally wrongly assume compactness in favour of readability. I'll learn one day :)

      I forgot to check the numbers were <=255, mainly because unpack "N" that I use mostly assumes they are zero if above 255, which is fine for my application

      I ran into undefined variables while iterating through a multidimensional hash containing all sort of server info using that function. I silenced them with a simple modification (checking if the parts of the IP is "defined"):
      sub IP_valid { my $ip = shift; $ip =~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; foreach ($1,$2,$3,$4){ if ( defined $_ && $_ <256 && $_ >0) {next;} return 0; } return 1; }
      Sure, that could have been added earlier but that if test was already checking multiple factors so it was easier to add. Thanks for this simple write-up! Just what I was looking for. Wow, 15 years later.

        Please also see Regexp::Common and Regexp::Common::net.

        c:\@Work\Perl\monks>perl -wMstrict -le "use Test::More 'no_plan'; use Test::NoWarnings; ;; VECTOR: for my $ar_vector ( [ '1.133.123.123', 1 ], [ '1.2.3.4', 1 ], [ '11.22.33.44', 1 ], [ '0.0.0.0', 1 ], [ '255.255.255.255', 1 ], [ '', '' ], [ '1asdf1.133.123.123', '' ], [ '12341.133.', '' ], [ '0.0.0.256', '' ], [ 'not at all valid', '' ], [ '1.2.3.4.5', '' ], [ '1111.2.3.1111', '' ], ) { my ($ip, $valid) = @$ar_vector; ;; my $status = $valid ? 'valid' : 'INVALID'; is IP_valid($ip), $valid, qq{'$ip' $status}; } ;; done_testing; ;; ;; use Regexp::Common qw(net); ;; sub IP_valid { my ($ip) = @_; return $ip =~ m{ \A $RE{net}{IPv4} \z }xms; } " ok 1 - '1.133.123.123' valid ok 2 - '1.2.3.4' valid ok 3 - '11.22.33.44' valid ok 4 - '0.0.0.0' valid ok 5 - '255.255.255.255' valid ok 6 - '' INVALID ok 7 - '1asdf1.133.123.123' INVALID ok 8 - '12341.133.' INVALID ok 9 - '0.0.0.256' INVALID ok 10 - 'not at all valid' INVALID ok 11 - '1.2.3.4.5' INVALID ok 12 - '1111.2.3.1111' INVALID 1..12 ok 13 - no warnings 1..13
        Note that, by design,  $RE{net}{IPv4} is not "anchored", so by itself it will match, e.g.:
        c:\@Work\Perl\monks>perl -wMstrict -le "use Regexp::Common qw(net); ;; print qq{matches IP of '$1'} if '99999.1.2.9999' =~ m{ ($RE{net}{IPv4}) }xms; " matches IP of '99.1.2.99'
        which may or may not be what you want. The  \A \z anchors in the example code above address this (quite intentional) feature.


        Give a man a fish:  <%-{-{-{-<

Re: IPv4 regex
by BlueLines (Hermit) on Feb 19, 2001 at 04:13 UTC
    There's a good discussion of this here.

    BlueLines

    Disclaimer: This post may contain inaccurate information, be habit forming, cause atomic warfare between peaceful countries, speed up male pattern baldness, interfere with your cable reception, exile you from certain third world countries, ruin your marriage, and generally spoil your day. No batteries included, no strings attached, your mileage may vary.
Re: IPv4 regex
by Tuna (Friar) on Feb 18, 2001 at 19:43 UTC
    Not sure if it matters to your program, but within the range of "numerically valid" IP addresses that everyone's suggestions capture, there are some ranges that are reserved by certain protocols. For instance, the subnet 224.0.0.0, is reserved for Cisco multicast group addresses. There are other examples, as well, such as reserved private subnets 172.0.0.0 and 192.0.0.0. These subnets are not publicly routable. There are other examples, as well.
    Just thought that I would kick in my $.02, in case it mattered
    Update:As merlyn has adeptly pointed out, in an indirect fashion, Tuna should NOT post before getting caffeine into his system. Sorry for the misinformation.
      It's not 172.* and 192.*. According to rfc1918:
      The Internet Assigned Numbers Authority (IANA) has reserved the following three blocks of the IP address space for private internet +s: 10.0.0.0 - 10.255.255.255 (10/8 prefix) 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)

      -- Randal L. Schwartz, Perl hacker

Re: IPv4 regex correction
by ryan (Pilgrim) on Feb 18, 2001 at 17:44 UTC
    Correction sorry,

    The double brackets are because I wasn't paying attention when I cut and pasted it and proceeded to comment on it.

    My regex should read:
    /^(\d{1,3}\.){3}\d{1,3}$/

    *yawn* :)