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

I'm hating myself for not being able to figure this one out. I've hit super search several times this morning, but was unable to locate anything on point.

I have the following example as an input file:

from 200.184.31.70/1713 to 168.49.127.83/80 on interface from 66.65.121.23/500 to 168.49.143.217/500 on interface from 208.40.56.131 to 168.49.123.100 on interface
What I'm trying to do is pull the IP addresses out, and if a port is specified, pull that as well. (The port is anything following the / in the IP address).

I've come up with the following regex.

$inLine =~ /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(\/[0-9]+)?)[\w]*([0-9]+\.[ +0-9]+\.[0-9]+\.[0-9]+(\/[0-9]+)?)/

The problem seems to be where I am trying to specify the optional / and port. If anyone can point me in the right direction, I'd really appreciate it. I'm pretty sure I'm overlooking something simple.

Thanks!

If things get any worse, I'll have to ask you to stop helping me.

Replies are listed 'Best First'.
Re: Regex for IP and Optional Port
by andye (Curate) on Jan 15, 2002 at 21:22 UTC
    Lordy! That's a bit long!

    I'd go for something like this:

    my ($ip1, $port1, $ip2, $port2) = ($inLine =~ m|^from ([0-9.]+)/?([0-9]*) to ([0-9.]+)/?([0-9]*) on in +terface$|)
    (Untested but should be roughly right)

    hth,
    andy.

    update: changed the delimiter for obvious reasons

Re: Regex for IP and Optional Port
by dmmiller2k (Chaplain) on Jan 15, 2002 at 22:33 UTC

    Save yourself some regex complexity: use \d rather than [0-9], and you can also be more specific about the number of digits you expect. You should first extract the IP addresses (optionally with port numbers), and then try to separate them in a subsequent step. For example,

    if ( $inLine =~ / ( # $1 - first IP \d{1,3} # 1-3 digits (?: # grouping for rep count \. # dot \d{1,3} # 1-3 digits ){3} # three more times (?: # optional extra grouping \/ # a slash \d+ # 1st port number )? # close extra grouping ) # close group around 1st IP \s+ # whitespace \w+ # a word \s+ # more whitespace ( # $2 - 2nd IP \d{1,3} # 1-3 digits (?: # grouping for rep count \. # dot \d{1,3} # 1-3 digits ){3} # three more times (?: # optional extra grouping \/ # a slash \d+ # 2nd port number )? # close extra grouping ) # close group around 2nd IP /x ) # specify /x { # matched - now check for port numbers my @ips = ( $1, $2 ); my @ports; foreach ( @ips ) { my ( $ip, $port ) split m{/}; # try to split IP on '/' push @ports, $port; # may be undef $_ = $ip if $port; # replace IP if got port } }

    dmm

    If you GIVE a man a fish you feed him for a day
    But,
    TEACH him to fish and you feed him for a lifetime
(tye)Re: Regex for IP and Optional Port
by tye (Sage) on Jan 16, 2002 at 00:02 UTC

    No one mentioned why yours didn't work. The only real problem is that the spaces in " to " don't match \w.

            - tye (but my friends call me "Tye")
(shockme) Re: Regex for IP and Optional Port
by shockme (Chaplain) on Jan 16, 2002 at 02:10 UTC
    Thanks for the insight, everyone. After studying andye's and dmmiller2k's suggestions, I was able to come up with a somewhat functioning framework. It was tye's comment that really brought it all home, though. \w does not include spaces, but it does include numbers. Doh! (Upon reflection, my example input data file is a very poor example, indeed.) The solution lied in constructing a character class of [\s\Wa-zA-Z]+.

    For completeness' sake, following is the final regex:

    ( # $1 - first IP \d{1,3} # 1-3 digits (?: # grouping for rep count \. # dot \d{1,3} # 1-3 digits ){3} # three more times (?: # optional extra grouping \/ # a slash \d+ # 1st port number )? # close extra grouping ) # close group around 1st IP [ # set up character class \s # whitespace a-zA-Z # letters \W # non-word characters ]+ # end character class ( # $2 - 2nd IP \d{1,3} # 1-3 digits (?: # grouping for rep count \. # dot \d{1,3} # 1-3 digits ){3} # three more times (?: # optional extra grouping \/ # a slash \d+ # 2nd port number )? # close extra grouping ) # close group around 2nd IP /x ) # specify /x

    Thanks again!

    Update: Fiddled with the grammer.

    If things get any worse, I'll have to ask you to stop helping me.

      Keep in mind that a valid IP address is matched by this beast, as found in the Ram Book:
      m/^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\. ([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/;
      Since this is a rather unwieldy expression, and rather static, it should be quite easy to compile it once with qr// and insert it as needed.

      -- Dave
Re: Regex for IP and Optional Port
by KILNA (Acolyte) on Jan 16, 2002 at 09:47 UTC
    I started wondering how short this could get... I came up with the following:
    $inLine =~ m|([\d\.]+)(/(\d*))?\D+([\d\.]+)(/(\d*))?|;
    Results are in $1,$3,$4 and $6
      Actually, borrowing some syntax from andye I got:
      $inLine =~ m|([\d.]+)/?(\d*)\D+([\d.]+)/?(\d*)|;
      And the results are even in $1,$2,$3,$4. Wooooo! :)