Поиск по этому блогу

пятница, 9 августа 2013 г.

Преобразование IPv4-адресов в Perl

В дополнение к предыдущему, религиозно верному, решению по сортировке ip-адресов отдельно хочется отметить решение "из коробки" cpan - модуль Sort::Key::IPv4.
Установить его можно либо из репозитория дистрибутива Linux, либо непосредственно с cpan:
# cpan
cpan[1]> install Sort::Key::IPv4
CPAN: Storable loaded ok (v2.39)
Reading '/home/olden/.cpan/Metadata'
--- 8< --- skip --- 8< ---
  sudo /usr/bin/make install  -- OK

cpan[2]> exit
Возьмём наш предыдущий набор ip-адресов:
my @array = ("127.0.0.1","212.90.160.8", "10.100.175.11", "10.10.10.8", "212.90.160.33","212.90.160.40");
my %hash = ("127.0.0.1" => "1", "212.90.160.8" => "0", "10.100.175.11" => "1", "10.10.10.8" => "1", "212.90.160.33" => "0", "212.90.160.40" => [ "1", "2" ] );
И отсортируем его используя функционал модуля Sort::Key::IPv4
foreach my $ip ( ipv4sort @array ) {
    printf("%s\n", $ip);
}
и, соответственно, для ключей хеша:
foreach my $ip ( ipv4sort keys %hash ) {
    printf("%s => %s\n", $ip, $hash{$ip});
}

вторник, 6 августа 2013 г.

Преобразование IPv4-адресов в Perl

К примеру, имеем такие массив и хеш:
my @array = ("127.0.0.1","212.90.160.8", "10.100.175.11", "10.10.10.8", "212.90.160.33","212.90.160.40");
my %hash = ("127.0.0.1" => "1", "212.90.160.8" => "0", "10.100.175.11" => "1", "10.10.10.8" => "1", "212.90.160.33" => "0", "212.90.160.40" => [ "1", "2" ] );
Хотим:
  • научиться быстренько преобразовывать каждый ip-адрес либо в числовой вид, либо просто в набор из четырёх байт;
  • хотим быстро, без лишних накладных расходов, вывести отсортированные значения ip-адресов из массива;
  • также хотим быстро, без лишних накладных расходов, вывести отсортированные значения ip-адресов, которые являются ключами хеша.
Преобразования из ip-адреса в число, из ip-адреса в набор символов и обратно можно осуществить, например, следующим образом:
map { 
    # Преобразовать ip-адрес в число
    my $n = unpack("N",pack("C4",split /\./));

    # Преобразовать число в ip-адрес
    print join(".",unpack("C4",pack("N",$n))),"\n";

    # Преобразовать ip-адрес в набор символов
    my $c = pack("C4",split /\./);

    # Преобразовать набор символов в ip-адрес
    print join(".",unpack("C4", $c)),"\n";
} @array;

Для того чтобы вывести отсортированный массив ip-адресов очевидным, однако не самым эффективным, является следующее решение:
foreach my $ip ( sort {
    my @a = split /\./, $a; my @b = split /\./, $b;
    return $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3];
    } @array ) {
    printf("%s\n", $ip);
}
Используем другое, более элегантное решение, выведем отсортированный массив ip-адресов, при используя преобразование GRT (Guttman-Rosler Transform) для того чтобы избежать вызова пользовательской функции в sort. Подробнее об этом можно прочитать в Pragmatic Perl — Выпуск 4. Июнь 2013 в статье "Сортировка в Perl".
foreach my $ip ( map { join(".",unpack("C4")) } sort map { pack("C4",split /\./) } @array ) {
    printf("%s\n", $ip);
}

Для того чтобы отсортировать по ключу хеша слегка преобразуем предыдущий цикл:
foreach my $ip ( map { join(".",unpack("C4")) } sort map { pack("C4",split /\./) } keys %hash ) {
    printf("%s => %s\n", $ip, $hash{$ip});
}