Beefy Boxes and Bandwidth Generously Provided by pair Networks RobOMonk
Perl-Sensitive Sunglasses
 
PerlMonks  

Unsorted IP Addr list to sorted IP list with ranges

by blue_cowdawg (Prior)
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:

10.0.4.100 10.0.4.99 10.3.5.21 10.0.4.101 10.15.21.6
The expected output would look like:
10.0.4.99-10.0.4.101 10.3.5.21 10.15.21.6

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.

Comment on Unsorted IP Addr list to sorted IP list with ranges
Select or Download Code
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 Anonymous Monk on Sep 11, 2003 at 15:46 UTC
    use Socket; my @ip = qw( 10.0.4.100 10.0.4.99 10.3.5.21 10.0.4.101 10.15.21.6); @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";
      really good work.
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 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 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";
    __DATA__
    10.0.0.0/18
    10.0.64.0/18
    10.0.192.0/18
    10.0.160.0/19
    

    Which will, of course, output 10.0.0.0/17, 10.0.160.0/19, 10.0.192.0/18.

    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 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(10.0.4.100 10.0.4.99 10.3.5.21 10.0.4.101 10.15.21.6); my @sorted_IP=map{substr($_,4) } sort map{pack('C4',/(\d+)\.(\d+)\.(\d+)\.(\d+)/).$_}@IP;

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

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://290709]
Approved by ybiC
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (5)
As of 2014-04-18 00:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (460 votes), past polls