5

我有一堆存储在数组中的 IP 地址,例如:

my @ip = qw(10.11.1.1 10.100.1.1 ...);

如何按升序对地址进行排序?我尝试了一个简单sort的方法,但它当然失败了。

4

8 回答 8

12

IPv4 地址只是 32 位数字。

use Socket qw( inet_aton );
my @sorted =
    map substr($_, 4),
       sort
          map inet_aton($_) . $_,
             @ips;

或者

my @sorted =
    map substr($_, 4),
       sort
          map pack('C4a*', split(/\./), $_),
             @ips;

第一个也接受域名。

于 2011-08-02T19:28:33.687 回答
8

我不喜欢任何假设它需要更多的解决方案。我以前曾被紧凑的符号所困扰,我想当 IPv6 变得更普遍时,这个问题会变得更加棘手。我只是让Net::IP弄清楚:

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;

这可以很好地处理紧凑和范围符号并eval捕获错误的 IP 地址。我不必分别对待 IPv4 和 IPv6:

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
于 2011-08-02T23:24:01.130 回答
1

使用排序::键::IPv4

于 2011-08-03T08:04:01.973 回答
1

只是 IPv4 地址?

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 );
于 2011-08-02T19:22:33.633 回答
1

我正在寻找@ikegami 的答案,结果证明它工作得很好,但我不知道为什么。所以我花了一些时间来弄清楚它背后的机制,我想分享我的笔记,以供较小的 Perl 专家将来参考......

在此示例中,我选择了两个非常具体的 IP 地址,因为当编码为 ASCII 时,它们看起来像ABCDEFGH,如该print Dumper()行的输出所示。

诀窍是在每个 IP 地址字符串前面加上 4 个字节,其中包含其二进制表示。然后对条目进行排序,最后再次删除前缀,留下排序后的 IP 地址列表。

内部工作原理在评论中描述,最好按编号顺序阅读。

#!/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";
}

输出将如下所示:

$VAR1 = 'EFGH69.70.71.72';
$VAR2 = 'ABCD64.65.66.67';
64.65.66.67
69.70.71.72

前两行print Dumper()显示 IP 地址以数字 IP 地址的 32 位表示为前缀。

于 2017-01-27T18:04:24.177 回答
1
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

你可以扭转它...... -r :-)

于 2019-09-05T14:32:03.593 回答
0

这应该给你一个好的开始:

#!/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";
于 2011-08-02T19:24:29.140 回答
0

有一个模块旨在对软件版本号进行排序。也许这会做你想要的?

于 2011-08-03T02:42:03.480 回答