Как мне отсортировать список IP-адресов в Perl? - PullRequest
4 голосов
/ 02 августа 2011

У меня есть куча IP-адресов, хранящихся в массиве, например ::10000

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

Как я могу отсортировать адреса в порядке возрастания? Я пробовал простой sort, но, конечно, не получилось.

Ответы [ 7 ]

11 голосов
/ 02 августа 2011

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;

Первый также принимает доменные имена.

6 голосов
/ 03 августа 2011

Мне не нравится какое-либо решение, которое предполагает больше, чем ему нужно. Раньше я был обожан подобными вещами благодаря компактной записи, и я думаю, что эта проблема усложняется, когда 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
1 голос
/ 27 января 2017

Я искал ответ @ ikegami, который сработал идеально, но я понятия не имел, почему.Поэтому я потратил пару мгновений, чтобы выяснить механику, лежащую в основе этого, и хочу поделиться своими заметками для дальнейшего использования для начинающих экспертов Perl ...

В этом примере я выбрал два очень специфических IP-адреса, потому что при кодировании какВ ASCII они будут выглядеть как ABCD и EFGH, как видно из вывода строки 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-адреса имеют префикс 32-битного представления числового IP-адреса.адреса.

1 голос
/ 03 августа 2011
1 голос
/ 02 августа 2011

Только 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 );
0 голосов
/ 03 августа 2011

Есть модуль, предназначенный для сортировки номеров версий программного обеспечения . Может быть, это будет делать то, что вы хотите?

0 голосов
/ 02 августа 2011

Это должно дать вам хорошее начало:

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