我如何编写不像C的Perl呢?

时间:2022-12-29 21:15:25

My co-workers complain that my Perl looks too much like C, which is natural since I program in C most of the time, and Perl just a bit. Here's my latest effort. I'm interest in Perl that is easy to understand. I'm a bit of a Perl critic, and have little tolerance for cryptic Perl. But with readability in mind, how could the following code be more Perlish?

我的同事抱怨说,我的Perl看起来太像C了,这是很自然的,因为我大部分时间都在用C编程,而Perl只是一点点。这是我的最新努力。我对Perl感兴趣,它很容易理解。我是一个Perl批评家,对晦涩难懂的Perl有点容忍。但是考虑到可读性,下面的代码怎么可能更有弹性呢?

It's goal is to do a traffic analysis and find which IP addresses are within the ranges given in the file "ips". Here's my effort:

它的目标是进行流量分析,并找出在“IP”文件中给定的范围内的IP地址。这是我的努力:

#!/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 个解决方案

#1


15  

Sometimes the most Perlish thing to do is to turn to CPAN instead of writing any code at all.

有时最棘手的事情是转向CPAN,而不是编写任何代码。

Here is a quick and dirty example using Net::CIDR::Lite and Net::IP::Match::Regexp:

这里有一个使用Net的快速和下流的例子::Lite和Net::IP::

#!/path/to/perl

use strict;
use warnings;

use English;
use IO::File;
use Net::CIDR::Lite;
use Net::IP::Match::Regexp qw(create_iprange_regexp match_ip);


my $cidr = Net::CIDR::Lite->new();

my $ips_fh = IO::File->new();

$ips_fh->open("ips") or die "Can't open 'ips': $OS_ERROR";

while (my $line = <$ips_fh>) {

    chomp $line;

    my ($start, $end) = split /,/, $line;

    my $range = join('-', $start, $end);

    $cidr->add_range($range);

}

$ips_fh->close();

my $regexp = create_iprange_regexp($cidr->list());

foreach my $traffic_fn (@ARGV) {

    my $traffic_fh = IO::File->new();

    $traffic_fh->open($traffic_fn) or die "Can't open '$traffic_fh': $OS_ERROR";

    while (my $ip_address = <$traffic_fh>) {

        chomp $ip_address;

        if (match_ip($ip_address, $regexp)) {
            print $ip_address, "\n";
        }     

    }

    $traffic_fh->close();

}

DISCLAIMER: I just banged that out, it's had minimal testing and no benchmarking. Sanity checks, error handling and comments omitted to keep the line count down. I didn't scrimp on the whitespace, though.

免责声明:我刚刚说过,它只有很少的测试,没有基准测试。完整性检查、错误处理和注释被省略,以保持行数减少。不过,我并没有在空格上浪费时间。

As for your code: There is no need to define your functions before you use them.

至于代码:在使用函数之前,不需要定义函数。

#2


24  

From years of seeing Perl code written by C programmers, here's some generic advice:

通过多年来看到C程序员编写的Perl代码,以下是一些通用的建议:

Use hashes. Use lists. USE HASHES! USE LISTS! Use list operations (map, grep, split, join), especially for small loops. Don't use fancy list algorithms; pop, splice, push, shift and unshift are cheaper. Don't use trees; hashes are cheaper. Hashes are cheap, make them, use them and throw them out! Use the iterator for loop, not the 3-arg one. Don't call things $var1, $var2, $var3; use a list instead. Don't call things $var_foo, $var_bar, $var_baz; use a hash instead. Use $foo ||= "default". Don't use $_ if you have to type it.

使用散列。使用列表。使用散列!使用列表!使用列表操作(map、grep、split、join),特别是对于小循环。不要使用花哨的列表算法;pop、splice、push、shift和unshift更便宜。不要使用树木;散列值是便宜。散列很便宜,做它们,用它们,然后扔出去!使用迭代器进行循环,而不是3-arg循环。不要称事物为$var1, $var2, $var3;使用列表。不要调用$var_foo, $var_bar, $var_baz;使用一个散列。使用$ foo | | =“默认”。如果必须输入$_,不要使用$_。

Don't use prototypes, IT'S A TRAP!!

不要使用原型,这是一个陷阱!!

Use regexes, not substr() or index(). Love regexes. Use the /x modifier to make them readable.

使用regexes,而不是substr()或index()。爱regex。使用/x修饰符使它们可读。

Write statement if $foo when you want a block-less conditional. There's almost always a better way to write a nested condition: try recursion, try a loop, try a hash.

当你想要一个无块的条件时,写if $foo。几乎总是有更好的方法来编写嵌套条件:尝试递归、尝试循环、尝试散列。

Declare variables when you need them, not at the top of the subroutine. use strict. use warnings, and fix them all. use diagnostics. Write tests. Write POD.

在需要时声明变量,而不是在子程序的顶部。使用严格的。使用警告,并修复它们。使用诊断。编写测试。写吊舱。

Use CPAN. Use CPAN! USE CPAN! Someone's probably already done it, better.

使用CPAN。使用CPAN !使用CPAN !也许有人已经做过了。

Run perlcritic. Run it with --brutal just for kicks. Run perltidy. Think about why you do everything. Change your style.

perlcritic运行。用野蛮的方式来对付它。上运行。想想你为什么要做每件事。改变你的风格。

Use the time not spent fighting the language and debugging memory allocation to improve your code.

使用未花费的时间来对抗语言和调试内存分配以改进代码。

Ask questions. Take style commentary on your code graciously. Go to a Perl Mongers meeting. Go onto perlmonks.org. Go to YAPC or a Perl Workshop. Your Perl knowledge will grow by leaps and bounds.

问问题。优雅地评论你的代码。请参加Perl Mongers会议。进入perlmonks.org。去YAPC或Perl研讨会。您的Perl知识将飞速增长。

#3


20  

Most of writing code to be "Perlish" would be taking advantage of the built-in functions in Perl.

大多数将代码编写为“Perlish”的做法都是利用Perl中的内置函数。

For instance, this:

例如,这个:

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++);
        }
    }
}   

I would rewrite something like:

我会重写一些东西,比如:

my @parts = split (/\./, $ip);

foreach my $part(reverse @parts){
  $part++;
  last unless ($part > 255 && !($part = 0));
}

That does what your code posted above does but is a little cleaner.

这和上面发布的代码所做的一样,但是更简洁一些。

Are you sure the code does what you want though? Just to me it looks a little strange that you only move to the previous 'part' of the IP if the one after it is > 255.

你确定这些代码是你想要的吗?对我来说,它看起来有点奇怪,如果你只移动到IP的前一部分,如果它是> 255。

#4


14  

Another example rewrite:

另一个例子重写:

sub is_less_than {
    my $left = shift; # I'm sure you just "forgot" to put the my() here...
    my $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
}

To this:

:

sub is_less_than {
    my @left = split(/\./, shift);
    my @right = split(/\./, shift);

    # one way to do it...
    for(0 .. 3) {
        if($left[$_] != $right[$_]) {
            return $left[$_] < $right[$_];
        }
    }

    # another way to do it - let's avoid so much indentation...
    for(0 .. 3) {
        return $left[$_] < $right[$_] if $left[$_] != $right[$_];
    }

    # yet another way to do it - classic Perl unreadable one-liner...
    $left[$_] == $right[$_] or return $left[$_] < $right[$_] for 0 .. 3;

    # just a note - that last one uses the short-circuit logic to condense
    # the if() statement to one line, so the for() can be added on the end.
    # Perl doesn't allow things like do_this() if(cond) for(0 .. 3); You
    # can only postfix one conditional. This is a workaround. Always use
    # 'and' or 'or' in these spots, because they have the lowest precedence.

    return 0 == 1; # false is not a keyword, or a boolean value.
    # though honestly, it wouldn't hurt to just return 0 or "" or undef()
}

Also, here:

另外,在这里:

my ($ip, $end_ip, $junk) = split /,/;

$junk might need to be @junk to capture all the junk, or you can probably leave it off - if you assign an unknown-sized array to an "array" of two elements, it will silently discard all the extra stuff. So

$ garbage可能需要是@ garbage才能捕获所有的垃圾,或者您可以将它关闭——如果您将一个未知大小的数组分配给一个由两个元素组成的“数组”,它将会默默地丢弃所有额外的东西。所以

my($ip, $end_ip) = split /,/;

And here:

在这里:

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

Instead of TRAFFIC, use a variable to store the filehandle. Also, in general, you should use exists() to check if a hash element exists, rather than defined() - it might exist but be set to undef (this shouldn't happen in your program, but it's a nice habit for when your program gets more complicated):

使用一个变量来存储文件句柄而不是流量。此外,一般来说,您应该使用exist()检查哈希元素是否存在,而不是定义()——它可能存在,但被设置为undef(这在您的程序中不应该发生,但当您的程序变得更复杂时,这是一个很好的习惯):

foreach (@ARGV) {
    open(my $traffic, $_) or die "Can't open $_ $OS_ERROR";
    while (<$traffic> ) {
        chomp;
        print "$_\n" if exists $addresses{$_};
    }
    # $traffic goes out of scope, and implicitly closes
}

Of course, you could also use Perl's wonderful <> operator, which opens each element of @ARGV for reading, and acts as a filehandle that iterates through them:

当然,您也可以使用Perl的奇妙的<>操作符,它打开@ARGV的每个元素进行读取,并作为文件句柄遍历它们:

while(<>) {
    chomp;
    print "$_\n" if exists $addresses{$_};
}

As has been noted before, try to avoid useing English unless you use English qw( -no_match_vars ); to avoid the significant performance penalty of those evil match_vars in there. And as hasn't been noted yet, but should be...

如前所述,尽量避免使用英语,除非你使用英语qw(-no_match_vars);为了避免那些邪恶的对手的显著性能损失。正如还没有提到的,但应该是……

ALWAYS ALWAYS ALWAYS always use strict; and use warnings; or else Larry Wall will descend from heaven and break your code. I see you have -w - this is enough, because even off of Unix, Perl parses the shebang line, and will find your -w and will use warnings; like it should. However, you need to use strict;. This will catch a lot of serious errors in your code, like not declaring variables with my or using false as a language keyword.

总是使用严格;和使用的警告;否则拉里·沃尔会从天堂降临并破坏你的代码。我看到你有-w -这就足够了,因为即使是在Unix下,Perl也会解析shebang行,并且会找到你的-w,并且会使用警告;应该喜欢它。但是,您需要使用严格;这将捕获代码中的许多严重错误,比如不使用my或使用false作为语言关键字。

Making your code work under strict as well as warnings will result in MUCH cleaner code that never breaks for reasons you can't figure out. You'll spend hours at the debugger debugging and you'll probably end up using strict and warnings anyway just to figure out what the errors are. Only remove them if (and only if) your code is finished and you're releasing it and it never generates any errors.

使您的代码在严格和警告下工作将导致更干净的代码,永远不会因为您无法理解的原因而中断。您将在调试器调试上花费数小时,您可能最终会使用严格的警告,仅仅是为了弄清楚这些错误是什么。只有当(且仅当)您的代码完成并且您正在释放它并且它从不产生任何错误时才删除它们。

#5


13  

While doing this certainly is one way to do it in Perl.

当然,在Perl中这样做是一种方法。

use strict;
use warnings;

my $new_ip;
{
  my @parts = split ('\.', $ip);

  foreach my $part(reverse @parts){
    $part++;

    if( $part > 255 ){
      $part = 0;
      next;
    }else{
      last;
    }
  }
  $new_ip = join '.', reverse @parts;
}

This is how I would actually implement it.

这就是我实现它的方式。

use NetAddr::IP;

my $new_ip = ''.(NetAddr::IP->new($ip,0) + 1) or die;

#6


6  

I can't say that this solution will make your program more Perl-ish, but it might simplify your algorithm.

我不能说这个解决方案会使你的程序更加复杂,但它可能会简化你的算法。

Rather than treating an IP address as a dotted-quad, base-256 number which needs the nested-if structure to implement the increment function, consider an IP address to be a 32-bit integer. Convert an IP of the form a.b.c.d into an integer with this (not tested):

与其把一个IP地址当作一个点阵四边形,不如把一个IP地址看成一个32位的整数。转换a.b.c格式的IP。d为一个整数(未经过测试):

sub ip2int {
    my $ip = shift;
    if ($ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        return ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
    } else {
        return undef;
    }
}

Now it's easy to determine if an IP falls between two endpoint IPs. Just do simple integer arithmetic and comparisons.

现在很容易确定一个IP是否落在两个端点IP之间。只需要做简单的整数算术和比较。

$begin = "192.168.5.0";
$end = "192.168.10.255";
$target = "192.168.6.2";
if (ip2int($target) >= ip2int($begin) && ip2int($target) <= ip2int($end)) {
    print "$target is between $begin and $end\n";
} else {
    print "$target is not in range\n";
}

#7


5  

Tell your coworkers that their perl looks too much like line noise. Please don't obfuscate your code just for the sake of obfuscation - it's asinine development goals like that which give perl such a bad reputation for being unreadable, when it's really bad programmers (apparently, like your coworkers) who write sloppy code. Nicely structured, indented, and logical code is a good thing. C is a good thing.

告诉您的同事,他们的perl看起来太像行噪声。请不要仅仅为了混淆而混淆您的代码——这是一个像这样的asinine开发目标,这给perl带来了难以读的坏名声,而真正糟糕的程序员(显然,就像您的同事一样)编写草率的代码。良好的结构化、缩进和逻辑代码是一件好事。C是一件好事。

Seriously, though - the best place to figure out how to write perl is in the O'Reilly "Perl Best Practices", by Damian Conway. It tells you how he thinks you should do things, and he always gives good reasons for his position as well as occasionally giving good reasons to disagree. I do disagree with him on some points, but his reasoning is sound. The odds that you work with anyone who knows perl better than Mr. Conway are pretty slim, and having a printed book (or at least a Safari subscription) gives you some more solid backing for your arguments. Pick up a copy of the Perl Cookbook while you're at it, as looking at code examples for solving common problems should get you on the right track. I hate to say "buy the book", but those are exceptionally good books that any perl developer should read.

不过,认真地说,要弄清楚如何编写perl,最好的地方是Damian Conway的O'Reilly“perl最佳实践”。它告诉你他认为你应该怎么做,他总是为自己的立场给出很好的理由,偶尔也给出很好的理由来反对你。在某些问题上我的确不同意他的观点,但他的推理是正确的。与比康韦更了解perl的人共事的可能性非常小,拥有一本印刷书籍(或至少是Safari订阅)可以为您的论点提供一些更可靠的支持。在阅读Perl Cookbook时获取它的副本,因为查看用于解决常见问题的代码示例应该会使您走上正确的道路。我不想说“买书”,但那些书是任何perl开发人员都应该读的非常好的书。

With regards to your specific code, you're using foreach, $_, split with no parens, shift, etc. It looks plenty perl-ish to my eyes - which have been developing with perl for quite a while. One note, though - I hate the English module. If you must use it, do it like use English qw( -no_match_vars );. The match_vars option slows down regexp parsing measurably, and the $PREMATCH / $POSTMATCH variables it provides aren't usually useful.

关于您的特定代码,您正在使用foreach、$_、split With no parens、shift等等。在我看来,它看起来相当不错——用perl开发它已经有一段时间了。不过有一点要注意——我讨厌英语模块。如果必须使用它,请使用English qw(-no_match_vars);match_vars选项可测量地减慢了regexp解析,并且它提供的$PREMATCH / $POSTMATCH变量通常不太有用。

#8


4  

There is only 1 advice: use strict. Rest of it is hardly relevant.

这里只有一条建议:使用严格。其余的几乎都无关紧要。

#9


3  

I know exactly how you feel. My first language was FORTRAN and like a good FORTRAN programmer, I wrote FORTRAN in every language since :).

我知道你的感受。我的第一语言是FORTRAN语言,就像一个优秀的FORTRAN语言程序员,从那时起,我就在每一种语言中编写FORTRAN语言。

I have this really wonderful book Effective Perl Programming that I keep re-reading every now and then. Especially a chapter called "Idiomatic Perl". Here are a few things I use to keep my Perl looking like Perl: List Operators like for map and grep, slices and hash slices, the quote operators.

我有这本非常棒的有效的Perl编程书,我经常反复阅读它。特别是一个叫做“惯用Perl”的章节。下面是一些我用来让Perl看起来像Perl的东西:列表操作符,比如map和grep,切片和散列切片,引号操作符。

Another thing that keeps my Perl from looking like FORTRAN/C is a regular reading of module sources especially those of the masters.

另一件让我的Perl看起来不像FORTRAN/C的事情是定期读取模块源代码,特别是那些master的源代码。

#10


2  

You could use Acme::Bleach or Acme::Morse

您可以使用Acme::Bleach或Acme::Morse。

#11


2  

While this would work:

而这将工作:

use strict;
use warnings;
use 5.010;

use NetAddr::IP;

my %addresses;
# Parse all the ip addresses and record them in a hash.
{
  open( my $ips_file, '<', 'ips') or die;

  local $_; # or my $_ on Perl 5.10 or later
  while( my $line = <$ips_file> ){
    my ($ip, $end_ip) = split ',', $line;
    next unless $ip and $end_ip;

    $ip     = NetAddr::IP->new( $ip, 0 ) or die;
    $end_ip = NetAddr::IP->new( $end_ip ) or die;
    while( $ip <= $end_ip ){
      $addresses{$ip->addr} = 1;
      $ip++;
    }
  }
  close $ips_file
}

# print IP addresses in any of the found ranges
use English;

for my $arg (@ARGV) {
  open(my $traffic, '<',$arg) or die "Can't open $arg $OS_ERROR";
  while( my $ip = <$traffic> ){
    chomp $ip;
    if( $addresses{$ip} ){
      say $ip
    }
  }
  close ($traffic);
}

I would if possible use netmasks, because it gets even simpler:

如果可能的话,我会使用netmask,因为它会变得更简单:

use Modern::Perl;
use NetAddr::IP;

my @addresses;
{
  open( my $file, '<', 'ips') or die;

  while( (my $ip = <$file>) =~ s(,.*){} ){
    next unless $ip;
    $ip = NetAddr::IP->new( $ip ) or die;
    push @addresses, $ip
  }

  close $file
}


for my $filename (@ARGV) {
  open( my $traffic, '<', $filename )
    or die "Can't open $filename";

  while( my $ip = <$traffic> ) {
    chomp $ip;
    next unless $ip;

    $ip = NetAddr::IP->new($ip) or next; # skip line on error
    my @match;
    for my $cmp ( @addresses ){
      if( $ip->within($cmp) ){
        push @match, $cmp;
        #last;
      }
    }

    say "$ip => @match" if @match;

    say "# no match for $ip" unless @match;
  }
  close ($traffic);
}

Test ips file:

测试ips文件:

192.168.0.1/24
192.168.0.0
0:0:0:0:0:0:C0A8:0/128

Test traffic file:

测试流量文件:

192.168.1.0
192.168.0.0
192.168.0.5

Output:

输出:

# no match for 192.168.1.0/32
192.168.0.0/32 => 192.168.0.1/24 192.168.0.0/32 0:0:0:0:0:0:C0A8:0/128
192.168.0.5/32 => 192.168.0.1/24

#12


1  

Instead of doing this :

而不是这样做:


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

you could do this :

你可以这样做:


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

Also, you could use the Fatal module, to avoid checking stuff for errors.

此外,还可以使用致命模块,以避免检查错误。

#13


1  

The only criteria I use for "how my code looks" is how easy it is to read and understand the purpose of the code (especially by programmers unfamiliar with Perl), not whether it follows a particular style.

我用于“我的代码看起来如何”的唯一标准是,阅读和理解代码的目的有多容易(尤其是不熟悉Perl的程序员),而不是它是否遵循特定的样式。

If a Perl language feature makes some logic easier to understand then I use it, if not I don't - even if it can do it in less code.

如果一个Perl语言特性使一些逻辑更容易理解,那么我就使用它,如果不是的话,即使它可以用更少的代码完成它。

Your co-workers may think my code is extremely "un perl-ish", but I'll bet they understood exactly what the code is doing and could modify it to fix / extend it without any trouble:

你的同事可能会认为我的代码非常“不可思议”,但我敢打赌,他们完全理解代码在做什么,并可以修改它,从而毫不费力地修复/扩展它:

my version:

我的版本:

#******************************************************************************
# 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);
   }

(Note: the extra "#" lines are in there to preserve my freakin' spacing, which always gets whacked when posting code here)

(注意:额外的“#”行是为了保持我的奇怪的空格,在这里写代码时总是会出错)

#14


0  

Am I missing something... will any of the above array versions work? The mods are performed on variables local to the for loop. I think Brad Gilbert's Net::IP solution would be my choice. Chris Lutz pretty much cleaned the rest the way I would've.

我少了什么……上面的任何一个数组版本都可以工作吗?在for循环的局部变量上执行mods。我认为Brad Gilbert的Net:::IP解决方案将是我的选择。克里斯·鲁兹用我的方式清理了剩下的部分。

As an aside - some of the comments about readability strike me as curious. Are there fewer [vigorous] complaints about the readability of Erlang/Lisp syntax because there is ONLY ONE way to write code in them?

顺便说一句,一些关于可读性的评论让我很好奇。对于Erlang/Lisp语法的可读性(因为只有一种方式在其中编写代码)的抱怨是否较少?

#15


0  

This is probably more like C, but is also more simple:

这可能更像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";

Update: I posted this before reading all of the code in the question. The code here just does the incrementing of the ip address.

更新:我在阅读问题中的所有代码之前发布了这个。这里的代码只执行ip地址的递增。

#1


15  

Sometimes the most Perlish thing to do is to turn to CPAN instead of writing any code at all.

有时最棘手的事情是转向CPAN,而不是编写任何代码。

Here is a quick and dirty example using Net::CIDR::Lite and Net::IP::Match::Regexp:

这里有一个使用Net的快速和下流的例子::Lite和Net::IP::

#!/path/to/perl

use strict;
use warnings;

use English;
use IO::File;
use Net::CIDR::Lite;
use Net::IP::Match::Regexp qw(create_iprange_regexp match_ip);


my $cidr = Net::CIDR::Lite->new();

my $ips_fh = IO::File->new();

$ips_fh->open("ips") or die "Can't open 'ips': $OS_ERROR";

while (my $line = <$ips_fh>) {

    chomp $line;

    my ($start, $end) = split /,/, $line;

    my $range = join('-', $start, $end);

    $cidr->add_range($range);

}

$ips_fh->close();

my $regexp = create_iprange_regexp($cidr->list());

foreach my $traffic_fn (@ARGV) {

    my $traffic_fh = IO::File->new();

    $traffic_fh->open($traffic_fn) or die "Can't open '$traffic_fh': $OS_ERROR";

    while (my $ip_address = <$traffic_fh>) {

        chomp $ip_address;

        if (match_ip($ip_address, $regexp)) {
            print $ip_address, "\n";
        }     

    }

    $traffic_fh->close();

}

DISCLAIMER: I just banged that out, it's had minimal testing and no benchmarking. Sanity checks, error handling and comments omitted to keep the line count down. I didn't scrimp on the whitespace, though.

免责声明:我刚刚说过,它只有很少的测试,没有基准测试。完整性检查、错误处理和注释被省略,以保持行数减少。不过,我并没有在空格上浪费时间。

As for your code: There is no need to define your functions before you use them.

至于代码:在使用函数之前,不需要定义函数。

#2


24  

From years of seeing Perl code written by C programmers, here's some generic advice:

通过多年来看到C程序员编写的Perl代码,以下是一些通用的建议:

Use hashes. Use lists. USE HASHES! USE LISTS! Use list operations (map, grep, split, join), especially for small loops. Don't use fancy list algorithms; pop, splice, push, shift and unshift are cheaper. Don't use trees; hashes are cheaper. Hashes are cheap, make them, use them and throw them out! Use the iterator for loop, not the 3-arg one. Don't call things $var1, $var2, $var3; use a list instead. Don't call things $var_foo, $var_bar, $var_baz; use a hash instead. Use $foo ||= "default". Don't use $_ if you have to type it.

使用散列。使用列表。使用散列!使用列表!使用列表操作(map、grep、split、join),特别是对于小循环。不要使用花哨的列表算法;pop、splice、push、shift和unshift更便宜。不要使用树木;散列值是便宜。散列很便宜,做它们,用它们,然后扔出去!使用迭代器进行循环,而不是3-arg循环。不要称事物为$var1, $var2, $var3;使用列表。不要调用$var_foo, $var_bar, $var_baz;使用一个散列。使用$ foo | | =“默认”。如果必须输入$_,不要使用$_。

Don't use prototypes, IT'S A TRAP!!

不要使用原型,这是一个陷阱!!

Use regexes, not substr() or index(). Love regexes. Use the /x modifier to make them readable.

使用regexes,而不是substr()或index()。爱regex。使用/x修饰符使它们可读。

Write statement if $foo when you want a block-less conditional. There's almost always a better way to write a nested condition: try recursion, try a loop, try a hash.

当你想要一个无块的条件时,写if $foo。几乎总是有更好的方法来编写嵌套条件:尝试递归、尝试循环、尝试散列。

Declare variables when you need them, not at the top of the subroutine. use strict. use warnings, and fix them all. use diagnostics. Write tests. Write POD.

在需要时声明变量,而不是在子程序的顶部。使用严格的。使用警告,并修复它们。使用诊断。编写测试。写吊舱。

Use CPAN. Use CPAN! USE CPAN! Someone's probably already done it, better.

使用CPAN。使用CPAN !使用CPAN !也许有人已经做过了。

Run perlcritic. Run it with --brutal just for kicks. Run perltidy. Think about why you do everything. Change your style.

perlcritic运行。用野蛮的方式来对付它。上运行。想想你为什么要做每件事。改变你的风格。

Use the time not spent fighting the language and debugging memory allocation to improve your code.

使用未花费的时间来对抗语言和调试内存分配以改进代码。

Ask questions. Take style commentary on your code graciously. Go to a Perl Mongers meeting. Go onto perlmonks.org. Go to YAPC or a Perl Workshop. Your Perl knowledge will grow by leaps and bounds.

问问题。优雅地评论你的代码。请参加Perl Mongers会议。进入perlmonks.org。去YAPC或Perl研讨会。您的Perl知识将飞速增长。

#3


20  

Most of writing code to be "Perlish" would be taking advantage of the built-in functions in Perl.

大多数将代码编写为“Perlish”的做法都是利用Perl中的内置函数。

For instance, this:

例如,这个:

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++);
        }
    }
}   

I would rewrite something like:

我会重写一些东西,比如:

my @parts = split (/\./, $ip);

foreach my $part(reverse @parts){
  $part++;
  last unless ($part > 255 && !($part = 0));
}

That does what your code posted above does but is a little cleaner.

这和上面发布的代码所做的一样,但是更简洁一些。

Are you sure the code does what you want though? Just to me it looks a little strange that you only move to the previous 'part' of the IP if the one after it is > 255.

你确定这些代码是你想要的吗?对我来说,它看起来有点奇怪,如果你只移动到IP的前一部分,如果它是> 255。

#4


14  

Another example rewrite:

另一个例子重写:

sub is_less_than {
    my $left = shift; # I'm sure you just "forgot" to put the my() here...
    my $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
}

To this:

:

sub is_less_than {
    my @left = split(/\./, shift);
    my @right = split(/\./, shift);

    # one way to do it...
    for(0 .. 3) {
        if($left[$_] != $right[$_]) {
            return $left[$_] < $right[$_];
        }
    }

    # another way to do it - let's avoid so much indentation...
    for(0 .. 3) {
        return $left[$_] < $right[$_] if $left[$_] != $right[$_];
    }

    # yet another way to do it - classic Perl unreadable one-liner...
    $left[$_] == $right[$_] or return $left[$_] < $right[$_] for 0 .. 3;

    # just a note - that last one uses the short-circuit logic to condense
    # the if() statement to one line, so the for() can be added on the end.
    # Perl doesn't allow things like do_this() if(cond) for(0 .. 3); You
    # can only postfix one conditional. This is a workaround. Always use
    # 'and' or 'or' in these spots, because they have the lowest precedence.

    return 0 == 1; # false is not a keyword, or a boolean value.
    # though honestly, it wouldn't hurt to just return 0 or "" or undef()
}

Also, here:

另外,在这里:

my ($ip, $end_ip, $junk) = split /,/;

$junk might need to be @junk to capture all the junk, or you can probably leave it off - if you assign an unknown-sized array to an "array" of two elements, it will silently discard all the extra stuff. So

$ garbage可能需要是@ garbage才能捕获所有的垃圾,或者您可以将它关闭——如果您将一个未知大小的数组分配给一个由两个元素组成的“数组”,它将会默默地丢弃所有额外的东西。所以

my($ip, $end_ip) = split /,/;

And here:

在这里:

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

Instead of TRAFFIC, use a variable to store the filehandle. Also, in general, you should use exists() to check if a hash element exists, rather than defined() - it might exist but be set to undef (this shouldn't happen in your program, but it's a nice habit for when your program gets more complicated):

使用一个变量来存储文件句柄而不是流量。此外,一般来说,您应该使用exist()检查哈希元素是否存在,而不是定义()——它可能存在,但被设置为undef(这在您的程序中不应该发生,但当您的程序变得更复杂时,这是一个很好的习惯):

foreach (@ARGV) {
    open(my $traffic, $_) or die "Can't open $_ $OS_ERROR";
    while (<$traffic> ) {
        chomp;
        print "$_\n" if exists $addresses{$_};
    }
    # $traffic goes out of scope, and implicitly closes
}

Of course, you could also use Perl's wonderful <> operator, which opens each element of @ARGV for reading, and acts as a filehandle that iterates through them:

当然,您也可以使用Perl的奇妙的<>操作符,它打开@ARGV的每个元素进行读取,并作为文件句柄遍历它们:

while(<>) {
    chomp;
    print "$_\n" if exists $addresses{$_};
}

As has been noted before, try to avoid useing English unless you use English qw( -no_match_vars ); to avoid the significant performance penalty of those evil match_vars in there. And as hasn't been noted yet, but should be...

如前所述,尽量避免使用英语,除非你使用英语qw(-no_match_vars);为了避免那些邪恶的对手的显著性能损失。正如还没有提到的,但应该是……

ALWAYS ALWAYS ALWAYS always use strict; and use warnings; or else Larry Wall will descend from heaven and break your code. I see you have -w - this is enough, because even off of Unix, Perl parses the shebang line, and will find your -w and will use warnings; like it should. However, you need to use strict;. This will catch a lot of serious errors in your code, like not declaring variables with my or using false as a language keyword.

总是使用严格;和使用的警告;否则拉里·沃尔会从天堂降临并破坏你的代码。我看到你有-w -这就足够了,因为即使是在Unix下,Perl也会解析shebang行,并且会找到你的-w,并且会使用警告;应该喜欢它。但是,您需要使用严格;这将捕获代码中的许多严重错误,比如不使用my或使用false作为语言关键字。

Making your code work under strict as well as warnings will result in MUCH cleaner code that never breaks for reasons you can't figure out. You'll spend hours at the debugger debugging and you'll probably end up using strict and warnings anyway just to figure out what the errors are. Only remove them if (and only if) your code is finished and you're releasing it and it never generates any errors.

使您的代码在严格和警告下工作将导致更干净的代码,永远不会因为您无法理解的原因而中断。您将在调试器调试上花费数小时,您可能最终会使用严格的警告,仅仅是为了弄清楚这些错误是什么。只有当(且仅当)您的代码完成并且您正在释放它并且它从不产生任何错误时才删除它们。

#5


13  

While doing this certainly is one way to do it in Perl.

当然,在Perl中这样做是一种方法。

use strict;
use warnings;

my $new_ip;
{
  my @parts = split ('\.', $ip);

  foreach my $part(reverse @parts){
    $part++;

    if( $part > 255 ){
      $part = 0;
      next;
    }else{
      last;
    }
  }
  $new_ip = join '.', reverse @parts;
}

This is how I would actually implement it.

这就是我实现它的方式。

use NetAddr::IP;

my $new_ip = ''.(NetAddr::IP->new($ip,0) + 1) or die;

#6


6  

I can't say that this solution will make your program more Perl-ish, but it might simplify your algorithm.

我不能说这个解决方案会使你的程序更加复杂,但它可能会简化你的算法。

Rather than treating an IP address as a dotted-quad, base-256 number which needs the nested-if structure to implement the increment function, consider an IP address to be a 32-bit integer. Convert an IP of the form a.b.c.d into an integer with this (not tested):

与其把一个IP地址当作一个点阵四边形,不如把一个IP地址看成一个32位的整数。转换a.b.c格式的IP。d为一个整数(未经过测试):

sub ip2int {
    my $ip = shift;
    if ($ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        return ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
    } else {
        return undef;
    }
}

Now it's easy to determine if an IP falls between two endpoint IPs. Just do simple integer arithmetic and comparisons.

现在很容易确定一个IP是否落在两个端点IP之间。只需要做简单的整数算术和比较。

$begin = "192.168.5.0";
$end = "192.168.10.255";
$target = "192.168.6.2";
if (ip2int($target) >= ip2int($begin) && ip2int($target) <= ip2int($end)) {
    print "$target is between $begin and $end\n";
} else {
    print "$target is not in range\n";
}

#7


5  

Tell your coworkers that their perl looks too much like line noise. Please don't obfuscate your code just for the sake of obfuscation - it's asinine development goals like that which give perl such a bad reputation for being unreadable, when it's really bad programmers (apparently, like your coworkers) who write sloppy code. Nicely structured, indented, and logical code is a good thing. C is a good thing.

告诉您的同事,他们的perl看起来太像行噪声。请不要仅仅为了混淆而混淆您的代码——这是一个像这样的asinine开发目标,这给perl带来了难以读的坏名声,而真正糟糕的程序员(显然,就像您的同事一样)编写草率的代码。良好的结构化、缩进和逻辑代码是一件好事。C是一件好事。

Seriously, though - the best place to figure out how to write perl is in the O'Reilly "Perl Best Practices", by Damian Conway. It tells you how he thinks you should do things, and he always gives good reasons for his position as well as occasionally giving good reasons to disagree. I do disagree with him on some points, but his reasoning is sound. The odds that you work with anyone who knows perl better than Mr. Conway are pretty slim, and having a printed book (or at least a Safari subscription) gives you some more solid backing for your arguments. Pick up a copy of the Perl Cookbook while you're at it, as looking at code examples for solving common problems should get you on the right track. I hate to say "buy the book", but those are exceptionally good books that any perl developer should read.

不过,认真地说,要弄清楚如何编写perl,最好的地方是Damian Conway的O'Reilly“perl最佳实践”。它告诉你他认为你应该怎么做,他总是为自己的立场给出很好的理由,偶尔也给出很好的理由来反对你。在某些问题上我的确不同意他的观点,但他的推理是正确的。与比康韦更了解perl的人共事的可能性非常小,拥有一本印刷书籍(或至少是Safari订阅)可以为您的论点提供一些更可靠的支持。在阅读Perl Cookbook时获取它的副本,因为查看用于解决常见问题的代码示例应该会使您走上正确的道路。我不想说“买书”,但那些书是任何perl开发人员都应该读的非常好的书。

With regards to your specific code, you're using foreach, $_, split with no parens, shift, etc. It looks plenty perl-ish to my eyes - which have been developing with perl for quite a while. One note, though - I hate the English module. If you must use it, do it like use English qw( -no_match_vars );. The match_vars option slows down regexp parsing measurably, and the $PREMATCH / $POSTMATCH variables it provides aren't usually useful.

关于您的特定代码,您正在使用foreach、$_、split With no parens、shift等等。在我看来,它看起来相当不错——用perl开发它已经有一段时间了。不过有一点要注意——我讨厌英语模块。如果必须使用它,请使用English qw(-no_match_vars);match_vars选项可测量地减慢了regexp解析,并且它提供的$PREMATCH / $POSTMATCH变量通常不太有用。

#8


4  

There is only 1 advice: use strict. Rest of it is hardly relevant.

这里只有一条建议:使用严格。其余的几乎都无关紧要。

#9


3  

I know exactly how you feel. My first language was FORTRAN and like a good FORTRAN programmer, I wrote FORTRAN in every language since :).

我知道你的感受。我的第一语言是FORTRAN语言,就像一个优秀的FORTRAN语言程序员,从那时起,我就在每一种语言中编写FORTRAN语言。

I have this really wonderful book Effective Perl Programming that I keep re-reading every now and then. Especially a chapter called "Idiomatic Perl". Here are a few things I use to keep my Perl looking like Perl: List Operators like for map and grep, slices and hash slices, the quote operators.

我有这本非常棒的有效的Perl编程书,我经常反复阅读它。特别是一个叫做“惯用Perl”的章节。下面是一些我用来让Perl看起来像Perl的东西:列表操作符,比如map和grep,切片和散列切片,引号操作符。

Another thing that keeps my Perl from looking like FORTRAN/C is a regular reading of module sources especially those of the masters.

另一件让我的Perl看起来不像FORTRAN/C的事情是定期读取模块源代码,特别是那些master的源代码。

#10


2  

You could use Acme::Bleach or Acme::Morse

您可以使用Acme::Bleach或Acme::Morse。

#11


2  

While this would work:

而这将工作:

use strict;
use warnings;
use 5.010;

use NetAddr::IP;

my %addresses;
# Parse all the ip addresses and record them in a hash.
{
  open( my $ips_file, '<', 'ips') or die;

  local $_; # or my $_ on Perl 5.10 or later
  while( my $line = <$ips_file> ){
    my ($ip, $end_ip) = split ',', $line;
    next unless $ip and $end_ip;

    $ip     = NetAddr::IP->new( $ip, 0 ) or die;
    $end_ip = NetAddr::IP->new( $end_ip ) or die;
    while( $ip <= $end_ip ){
      $addresses{$ip->addr} = 1;
      $ip++;
    }
  }
  close $ips_file
}

# print IP addresses in any of the found ranges
use English;

for my $arg (@ARGV) {
  open(my $traffic, '<',$arg) or die "Can't open $arg $OS_ERROR";
  while( my $ip = <$traffic> ){
    chomp $ip;
    if( $addresses{$ip} ){
      say $ip
    }
  }
  close ($traffic);
}

I would if possible use netmasks, because it gets even simpler:

如果可能的话,我会使用netmask,因为它会变得更简单:

use Modern::Perl;
use NetAddr::IP;

my @addresses;
{
  open( my $file, '<', 'ips') or die;

  while( (my $ip = <$file>) =~ s(,.*){} ){
    next unless $ip;
    $ip = NetAddr::IP->new( $ip ) or die;
    push @addresses, $ip
  }

  close $file
}


for my $filename (@ARGV) {
  open( my $traffic, '<', $filename )
    or die "Can't open $filename";

  while( my $ip = <$traffic> ) {
    chomp $ip;
    next unless $ip;

    $ip = NetAddr::IP->new($ip) or next; # skip line on error
    my @match;
    for my $cmp ( @addresses ){
      if( $ip->within($cmp) ){
        push @match, $cmp;
        #last;
      }
    }

    say "$ip => @match" if @match;

    say "# no match for $ip" unless @match;
  }
  close ($traffic);
}

Test ips file:

测试ips文件:

192.168.0.1/24
192.168.0.0
0:0:0:0:0:0:C0A8:0/128

Test traffic file:

测试流量文件:

192.168.1.0
192.168.0.0
192.168.0.5

Output:

输出:

# no match for 192.168.1.0/32
192.168.0.0/32 => 192.168.0.1/24 192.168.0.0/32 0:0:0:0:0:0:C0A8:0/128
192.168.0.5/32 => 192.168.0.1/24

#12


1  

Instead of doing this :

而不是这样做:


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

you could do this :

你可以这样做:


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

Also, you could use the Fatal module, to avoid checking stuff for errors.

此外,还可以使用致命模块,以避免检查错误。

#13


1  

The only criteria I use for "how my code looks" is how easy it is to read and understand the purpose of the code (especially by programmers unfamiliar with Perl), not whether it follows a particular style.

我用于“我的代码看起来如何”的唯一标准是,阅读和理解代码的目的有多容易(尤其是不熟悉Perl的程序员),而不是它是否遵循特定的样式。

If a Perl language feature makes some logic easier to understand then I use it, if not I don't - even if it can do it in less code.

如果一个Perl语言特性使一些逻辑更容易理解,那么我就使用它,如果不是的话,即使它可以用更少的代码完成它。

Your co-workers may think my code is extremely "un perl-ish", but I'll bet they understood exactly what the code is doing and could modify it to fix / extend it without any trouble:

你的同事可能会认为我的代码非常“不可思议”,但我敢打赌,他们完全理解代码在做什么,并可以修改它,从而毫不费力地修复/扩展它:

my version:

我的版本:

#******************************************************************************
# 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);
   }

(Note: the extra "#" lines are in there to preserve my freakin' spacing, which always gets whacked when posting code here)

(注意:额外的“#”行是为了保持我的奇怪的空格,在这里写代码时总是会出错)

#14


0  

Am I missing something... will any of the above array versions work? The mods are performed on variables local to the for loop. I think Brad Gilbert's Net::IP solution would be my choice. Chris Lutz pretty much cleaned the rest the way I would've.

我少了什么……上面的任何一个数组版本都可以工作吗?在for循环的局部变量上执行mods。我认为Brad Gilbert的Net:::IP解决方案将是我的选择。克里斯·鲁兹用我的方式清理了剩下的部分。

As an aside - some of the comments about readability strike me as curious. Are there fewer [vigorous] complaints about the readability of Erlang/Lisp syntax because there is ONLY ONE way to write code in them?

顺便说一句,一些关于可读性的评论让我很好奇。对于Erlang/Lisp语法的可读性(因为只有一种方式在其中编写代码)的抱怨是否较少?

#15


0  

This is probably more like C, but is also more simple:

这可能更像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";

Update: I posted this before reading all of the code in the question. The code here just does the incrementing of the ip address.

更新:我在阅读问题中的所有代码之前发布了这个。这里的代码只执行ip地址的递增。