Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Unsorted IP Addr list to sorted IP list with ranges

by blue_cowdawg (Monsignor)
on Sep 11, 2003 at 15:03 UTC ( #290709=perlquestion: print w/replies, xml ) Need Help??
blue_cowdawg has asked for the wisdom of the Perl Monks concerning the following question:

Under the heading of "I've seen how to do this in my travels by CRS has set in" I remember seeing an algorithm (or may have actually written the thing myself) to take an unsorted list of IP addresses, sort them and then substitute ranges for sequential addresess.

Just so I'm clear as to what I'm after, here's a small sample list:
The expected output would look like:

Has anybody done written anything like this or have an idea how to efficiently do this?

Peter L. Berghold -- Unix Professional
Peter at Berghold dot Net
   Dog trainer, dog agility exhibitor, brewer of fine Belgian style ales. Happiness is a warm, tired, contented dog curled up at your side and a good Belgian ale in your chalice.

Replies are listed 'Best First'.
Re: Unsorted IP Addr list to sorted IP list with ranges
by Anonymous Monk on Sep 11, 2003 at 15:46 UTC
    use Socket; my @ip = qw(; @ip = do { my $prev; map { defined($prev) && $prev == $_ ? () : ( $prev = $_ ) } sort { $a <=> $b } map { unpack "N", inet_aton $_ } @ip; }; for my $i ( 0 .. $#ip ) { if (!$i || $i == $#ip || $ip[$i-1] != $ip[$i] - 1 || $ip[$i+1] != $ip[$i] + 1 ) { print $ip[$i-1] == $ip[$i] - 1 ? "-" : " " if $i; print inet_ntoa( pack "N", $ip[$i] ); } } print "\n";
    Re: Unsorted IP Addr list to sorted IP list with ranges (CPAN module candidates)
    by ybiC (Prior) on Sep 11, 2003 at 15:37 UTC
    Re: Unsorted IP Addr list to sorted IP list with ranges
    by jonadab (Parson) on Sep 11, 2003 at 16:19 UTC

      The sorting part is easy:

      @sortedip = map { "$$_[0].$$_[1].$$_[2]$$_[3]" } sort { ($$a[0] <=> $$b[0]) or ($$a[1] <=> $$b[1]) or ($$a[2] <=> $$b[2]) or ($$a[3] <=> $$b[3]) } map { /(\d+)[.](\d+)[.](\d+)[.](\d+)/; [$1, $2, $3, $4] } @ip;

      Personally I would leave off that (typographically) first map, so that you get a list of arrayrefs; the range checking will probably be easier that way. Speaking of the range checking... you probably want to use a foreach loop over the now sorted list, in each case keeping track of the previous IP or range and pushing that if the current IP isn't contiguous. Something along these lines...

      @sorted = sort { ($$a[0] <=> $$b[0]) or ($$a[1] <=> $$b[1]) or ($$a[2] <=> $$b[2]) or ($$a[3] <=> $$b[3]) } map { /(\d+)[.](\d+)[.](\d+)[.](\d+)/; [$1, $2, $3, $4] } @ip; my $previous = []; my @range = (); for $current (@sorted, [260, 0, 0, 0]) { if (greaterbyone($previous,$current) { $$previous[4] = $$current[3]; } else { push @range, $previous; $previous = $current; }

      greaterbyone just has to return true if the IP address in its second argument is one greater than the IP address or range in its first argument. I'll leave that part as an exercise.

      $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
    Re: Unsorted IP Addr list to sorted IP list with ranges
    by zengargoyle (Deacon) on Sep 12, 2003 at 07:26 UTC

      from NetAddr::IP tutorial...

      Optimising the address space

      This is one of the reason for writing NetAddr::IP in the first place. Let's say you have a few chunks of IP space and you want to find the optimum CIDR representation for them. By optimum, I mean the least amount of CIDR subnets that exactly represent the given IP address space. The code below is an example of this:

      use NetAddr::IP;
      push @addresses, NetAddr::IP->new($_) for <DATA>;
      print join(", ", NetAddr::IP::compact(@addresses)), "\n";

      Which will, of course, output,,

      and also in the tutorial is enough to print those combined ranges in just about any format you can come up with.

    Re: Unsorted IP Addr list to sorted IP list with ranges
    by atcroft (Monsignor) on Sep 11, 2003 at 23:27 UTC

      If there is a better solution that uses established modules, I would suggest that. Barring that, here is some code I was toying with a while back to do something similar-probably not the best or most efficient way to do it, however. (Address data was in __DATA__, which I did not include here for length.) Hope maybe this might give you an idea or two (if nothing else, maybe of how not to do it).

    Re: Unsorted IP Addr list to sorted IP list with ranges
    by TStanley (Canon) on Sep 12, 2003 at 12:23 UTC
      davorg shows an interesting way of sorting IP addresses, using the Guttman-Rosler Transform in his book
      my @IP=qw(; my @sorted_IP=map{substr($_,4) } sort map{pack('C4',/(\d+)\.(\d+)\.(\d+)\.(\d+)/).$_}@IP;

      The only thing necessary for the triumph of evil is for good men to do nothing -- Edmund Burke

    Log In?

    What's my password?
    Create A New User
    Node Status?
    node history
    Node Type: perlquestion [id://290709]
    Approved by ybiC
    and all is quiet...

    How do I use this? | Other CB clients
    Other Users?
    Others pondering the Monastery: (3)
    As of 2017-02-22 01:14 GMT
    Find Nodes?
      Voting Booth?
      Before electricity was invented, what was the Electric Eel called?

      Results (322 votes). Check out past polls.