I have a bunch of IP-addres开发者_JAVA百科ses stored in an array, e.g.:
my @ip = qw(10.11.1.1 10.100.1.1 ...);
How can I sort the addresses in the ascending order? I've tried a simple sort
but it failed, of course.
IPv4 addresses are just 32-bit numbers.
use Socket qw( inet_aton );
my @sorted =
map substr($_, 4),
sort
map inet_aton($_) . $_,
@ips;
or
my @sorted =
map substr($_, 4),
sort
map pack('C4a*', split(/\./), $_),
@ips;
The first one also accepts domain names.
I'm not fond of any solution that assumes more that it needs. I've been burned on this sort of thing by compact notation before, and I imagine this problem gets tougher when IPv6 becomes more common. I'd just let Net::IP figure it out:
use 5.010;
use Net::IP;
my @ip = qw(
192.168.1.10
172.16.5.5
256.0.0.1
192.168.1/24
127.1
127.0.1
fd00:6587:52d7:f8f7:5a55:caff:fef5:af31
fd24:cd9b:f001:884c:5a55:caff:fef5:af31
);
my @sorted =
map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, eval { Net::IP->new( $_ )->intip } ] }
@ip;
say join "\n", @sorted;
This handles the compact and range notations just fine and the eval
catches the bad IP addresses. I don't have to treat IPv4 and IPv6 separately:
256.0.0.1
127.0.1
127.1
172.16.5.5
192.168.1/24
192.168.1.10
fd00:6587:52d7:f8f7:5a55:caff:fef5:af31
fd24:cd9b:f001:884c:5a55:caff:fef5:af31
use Sort::Key::IPv4
Just IPv4 addresses?
my @ip = map $_->[0],
sort { $a->[1] cmp $b->[1] }
map [ $_, join '', map chr, split /\./, $_ ],
qw( 10.1.2.3 172.20.1.2 192.168.1.2 );
I was looking @ikegami's answer which turned out to work perfectly, but I had no clue why. So I took couple moments to figure out the mechanics behind it and I want to share my notes for future reference for the lesser Perl experts ...
In this example I chose two very specific IP addresses because when encoded as ASCII they'll look like ABCD
and EFGH
, as seen by the output of the print Dumper()
line.
The trick is to prefix every IP-address string with 4 bytes containing its binary representation. Then the entries are sorted and finally the prefix is removed again, leaving a list of sorted IP-addresses.
The inner workings are described in the comments, best to read them in the numbered order.
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my @ips = qw( 69.70.71.72 65.66.67.68 );
print Dumper( map( pack( 'C4a*' , split( /\./ ) , $_ ) , @ips ) );
foreach my $ip (
map( # 5. For each IP address that was enriched with 32 bits representation ....
substr( $_ , 4) , # 6. Snip off the first four bytes as this is just a binary representation of the string, used for sorting.
sort( # 4. Sort will essentially work on the first 4 octets as these will represent the entire remainder of the string.
map( # 1. For each IP address in @ARGV ...
pack( 'C4a*' , # 3. Change ASCII encoded octets from the IP address into a 32 bit 'string' (that can be sorted) and append it with the original IP address string for later use.
split( /\./ ), $_ ) , # 2. Split the IP address in separate octets
@ips # 0. Unsorted list of IP addresses.
)
)
)
) {
print "$ip\n";
}
The output will look as follows:
$VAR1 = 'EFGH69.70.71.72';
$VAR2 = 'ABCD64.65.66.67';
64.65.66.67
69.70.71.72
Where the first two lines are from print Dumper()
which shows the IP- addresses are prefixed with a 32-bit representation of the numeric IP-addresses.
sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 filename
or | to sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4
and you can reverse it... -r :-)
This should give you a good start:
#!/usr/bin/perl
use strict;
use warnings;
sub Compare {
# TODO: Error checking
my @a = split /\./, $a;
my @b = split /\./, $b;
# TODO: This might be cleaner as a loop
return $a[0] <=> $b[0] ||
$a[1] <=> $b[1] ||
$a[2] <=> $b[2] ||
$a[3] <=> $b[3];
}
my @ip = qw( 172.20.1.2 10.10.2.3 10.1.2.3 192.168.1.2 );
@ip = sort Compare @ip;
print join("\n", @ip), "\n";
There's a module designed to sort software version numbers. Maybe that will do what you want?
精彩评论