话接上篇,ip整理出来,然后就是接一个ip地址,快速定位查找到它属于哪个ip段,然后返回具体的省份和运营商。因为之前的ip已经转换成数字,而且是顺序排列的,所以可以采用折半算法(二分法)。perl脚本如下:
```perl#!/usr/bin/perl -w
#my $ip = inet_aton(“$ARGV[0]”);
my $ip = inet_aton(get_test_ip());
my $file = $ARGV[1] || ‘iplist.txt’;
my $length = cat $file | wc -l
;
my $code = get_area_code(‘quhao.txt’); my @isplist = qw(‘其他’ ‘电信’ ‘联通’ ‘移动’ ‘教育网’);
open my $fh, ‘<’, “$file” or die “Cannot open $file\n”; my $line_len = ‘26’; #=10+10+4+1+1,包括回车符(注意上篇输出为了好看多了空格,可以删掉) my $first = 0; my $last = $length - 1; #统一使用SEEK_SET,所以最后一行的起始位置是length-1 my $result = 1; while ($result) { my $middle = sprintf(“%.0f”,($last-$first) / 2 + $first); #折半位置,除法取整时采用sprintf比直接int精确 seek $fh, $line_len * $middle, 0; #移动到折半位置 sysread $fh, $begin_ip, 10; #从折半处读取10位 sysread $fh, $end_ip, 10; #接着再读10位,如果没删空格,还要先seek移动1位,麻烦 #根据比大小决定下次向哪个方向折半 if ( $ip < $begin_ip ) { $last = $middle; next; } elsif ( $ip > $end_ip ) { $first = $middle; next; } else { #找到相应区间,读取区号和运营商号 sysread $fh, $area, 4; sysread $fh, $isp, 1; printf “%010s %s %s\n”, $ip, $code->{“$area”}, $isplist[$isp]; $result = 0; #设定$result为假,退出循环 }; };
close $fh;
sub inet_aton { my $ip = shift; my $short = sprintf “%010s”, $1 * 2563 + $2 * 2562 + $3 * 256 + $4 if $ip =~ /(\d+).(\d+).(\d+).(\d+)/; return $short; }; #对应区号和省份,跟上篇的kv相反 sub get_area_code { my $file = shift; my $area_code = { ‘0000’ => ‘other’ }; open my $fh,’<’,”$file” or die “Cannot open $file”; while (<$fh>) { chomp; my($area,$code) = split; $area_code->{“$code”} = “$area”; } close $fh; return $area_code; }; #生成一个随机的合法ip地址 sub get_test_ip { return join ‘.’, map int rand 256, 1..4; }```