Как я могу написать Perl, который не похож на C? - PullRequest
5 голосов
/ 11 апреля 2009

Мои коллеги жалуются, что мой Perl слишком похож на C, что естественно, так как я программирую на C большую часть времени, а Perl - немного. Вот мои последние усилия. Меня интересует Perl, который легко понять. Я немного критик Perl и мало толерантен к загадочному Perl. Но с учетом читабельности, как следующий код может быть более Perlish?

Его цель - провести анализ трафика и определить, какие IP-адреса находятся в пределах диапазонов, указанных в файле "ips". Вот мои усилия:

#!/usr/bin/perl -w

# Process the files named in the arguments, which will contain lists of IP addresses, and see if 
# any of them are in the ranges spelled out in the local file "ip", which has contents of the
# form start-dotted-quad-ip-address,end-dotted-quad-ip_address,stuff_to_be_ignored
use English;


open(IPS,"ips") or die "Can't open 'ips' $OS_ERROR";

# Increment a dotted-quad ip address
# Ignore the fact that part1 could get erroneously large.
sub increment {
    $ip = shift;

    my ($part_1, $part_2, $part_3, $part_4) = split (/\./, $ip);
    $part_4++;
    if ( $part_4 > 255 ) {
        $part_4 = 0;
        ($part_3++);
        if ( $part_3 > 255 ) {
            $part_3 = 0;
            ($part_2++);
            if ( $part_2 > 255 ) {
                $part_2 = 0;
                ($part_1++);
            }
        }
   }   
    return ("$part_1.$part_2.$part_3.$part_4");
}

# Compare two dotted-quad ip addresses.
sub is_less_than {
    $left = shift;
    $right = shift;

    my ($left_part_1, $left_part_2, $left_part_3, $left_part_4)     = split (/\./, $left);
    my ($right_part_1, $right_part_2, $right_part_3, $right_part_4) = split (/\./, $right);


    if  ($left_part_1 != $right_part_1 ) { 
        return ($left_part_1 < $right_part_1);
    }   
    if  ($left_part_2 != $right_part_2 ) { 
        return ($left_part_2 < $right_part_2);
    }   
    if  ($left_part_3 != $right_part_3 ) { 
        return ($left_part_3 < $right_part_3);
    }
    if  ($left_part_4 != $right_part_4 ) {
        return ($left_part_4 < $right_part_4);
    }
    return (false);  # They're equal
}

my %addresses;
# Parse all the ip addresses and record them in a hash.   
while (<IPS>) {
    my ($ip, $end_ip, $junk) = split /,/;
    while (is_less_than($ip, $end_ip) ) {
        $addresses{$ip}=1;
        $ip = increment($ip);
    }
}

# print IP addresses in any of the found ranges

foreach (@ARGV) {
    open(TRAFFIC, $_) or die "Can't open $_ $OS_ERROR";
    while (<TRAFFIC> ) {
        chomp;
        if (defined $addresses{$_}) {
            print "$_\n";
        }
    }
    close (TRAFFIC);

}

Ответы [ 15 ]

2 голосов
/ 11 апреля 2009

Вы можете использовать Acme :: Bleach или Acme :: Morse

1 голос
/ 11 апреля 2009

Единственный критерий, который я использую для «того, как выглядит мой код», - это насколько легко читать и понимать цель кода (особенно программистами, незнакомыми с Perl), а не то, следует ли он определенному стиль.

Если функция языка Perl облегчает понимание некоторой логики, тогда я использую ее, если нет - даже если она может делать это в меньшем количестве кода.

Ваши коллеги могут подумать, что мой код чрезвычайно «непоправимый», но держу пари, что они точно поняли, что делает код, и могут изменить его, чтобы исправить / расширить его без каких-либо проблем:

моя версия:

#******************************************************************************
# Load the allowable ranges into a hash
#******************************************************************************
my %ipRanges = loadIPAddressFile("../conf/ip.cfg");

#*****************************************************************************
# Get the IP to check on the command line
#*****************************************************************************
my ( $in_ip_address ) = @ARGV;

# Convert it to number for comparison
my $ipToCheckNum = 1 * sprintf("%03d%03d%03d%03d", split(/\./, $in_ip_address));

#*****************************************************************************
# Loop through the ranges and see if the number is in any of them
#*****************************************************************************
my $startIp;
my $endIp;
my $msg = "IP [$in_ip_address] is not in range.\n";

foreach $startIp (keys(%ipRanges))
   {
   $endIp = $ipRanges{$startIp};

   if ( $startIp <= $ipToCheckNum and $endIp >= $ipToCheckNum ) 
      {
      $msg = "IP [$in_ip_address] is in range [$startIp] to [$endIp]\n";
      }
   }

print $msg;

#******************************************************************************
# Function: loadIPAddressFile()
#   Author: Ron Savage
#     Date: 04/10/2009
# 
# Description:
# loads the allowable IP address ranges into a hash from the specified file.
# Hash key is the starting value of the range, value is the end of the range.
#******************************************************************************
sub loadIPAddressFile
   {
   my $ipFileHandle;
   my $startIP;
   my $endIP;
   my $startIPnum;
   my $endIPnum;
   my %rangeList;

   #***************************************************************************
   # Get the arguments sent
   #***************************************************************************
   my ( $ipFile ) = @_;

   if ( open($ipFileHandle, "< $ipFile") )
      {
      while (<$ipFileHandle>)
         {
         ( $startIP, $endIP ) = split(/\,/, $_ );

         # Convert them to numbers for comparison
         $startIPnum = 1 * sprintf("%03d%03d%03d%03d", split(/\./, $startIP));
         $endIPnum   = 1 * sprintf("%03d%03d%03d%03d", split(/\./, $endIP));

         $rangeList{$startIPnum} = $endIPnum;
         }

      close($ipFileHandle);
      }
   else
      {
      print "Couldn't open [$ipFile].\n";
      }

   return(%rangeList);
   }

(Примечание: здесь есть дополнительные строки "#", чтобы сохранить интервал, который я чертовски поражаю, который всегда вылетает при размещении кода здесь)

1 голос
/ 11 апреля 2009

Вместо этого:


if  ($left_part_1 != $right_part_1 ) { 
    return ($left_part_1 < $right_part_1);
}

Вы могли бы сделать это:


return $left_part_1 < $right_part_1 if($left_part_1 != $right_part_1);

Кроме того, вы можете использовать модуль Fatal , чтобы избежать проверки на наличие ошибок.

0 голосов
/ 13 апреля 2009

Это, вероятно, больше похоже на C, но также более простое:

use Socket qw(inet_aton inet_ntoa);

my $ip = ("192.156.255.255");

my $ip_1 = inet_ntoa(pack("N", unpack("N", inet_aton($ip))+1));
print "$ip $ip_1\n";

Обновление: я разместил это перед прочтением всего кода в вопросе. Код здесь просто увеличивает IP-адрес.

0 голосов
/ 12 апреля 2009

Я что-то упустил ... будет ли работать любая из вышеперечисленных версий массива? Моды выполняются на переменных, локальных для цикла for. Я думаю, что решение Брэда Гилберта Net :: IP было бы моим выбором. Крис Лутц вычистил все остальное так, как я бы.

В качестве отступления - некоторые комментарии о читабельности кажутся мне любопытными. Есть ли меньше [энергичных] жалоб на удобочитаемость синтаксиса Erlang / Lisp, потому что есть ТОЛЬКО ОДИН способ написать код в них?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...