<?
define('QQWRY' , 'qqwry/'.'qqwry.dat' ) ; function IpToInt($Ip) {     $array=explode('.',$Ip);     $Int=($array[0] * 256*256*256) + ($array[1]*256*256) + ($array[2]*256) + $array[3];     return $Int; } function IntToIp($Int) {     $b1=($Int & 0xff000000)>>24;     if ($b1<0) $b1+=0x100;     $b2=($Int & 0x00ff0000)>>16;     if ($b2<0) $b2+=0x100;     $b3=($Int & 0x0000ff00)>>8;     if ($b3<0) $b3+=0x100;     $b4= $Int & 0x000000ff;     if ($b4<0) $b4+=0x100;     $Ip=$b1.'.'.$b2.'.'.$b3.'.'.$b4;     return $Ip; } class TQQwry {     var $StartIP = 0;     var $EndIP   = 0;     var $Country = '';     var $Local   = '';     var $CountryFlag = 0; // 标识 Country位置 
                          // 0x01,随后3字节为Country偏移,没有Local 
                          // 0x02,随后3字节为Country偏移,接着是Local 
                          // 其他,Country,Local,Local有类似的压缩。可能多重引用。     var $fp;     var $FirstStartIp = 0;     var $LastStartIp = 0;     var $EndIpOff = 0 ;     function getStartIp ( $RecNo ) {         $offset = $this->FirstStartIp + $RecNo * 7 ;         @fseek ( $this->fp , $offset , SEEK_SET ) ;         $buf = fread ( $this->fp , 7 ) ;         $this->EndIpOff = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])* 256*256);         $this->StartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);         return $this->StartIp ;     }     function getEndIp ( ) {         @fseek ( $this->fp , $this->EndIpOff , SEEK_SET ) ;         $buf = fread ( $this->fp , 5 ) ;         $this->EndIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);         $this->CountryFlag = ord ( $buf[4] ) ;         return $this->EndIp ;     }     function getCountry ( ) {         switch ( $this->CountryFlag ) {             case 1:             case 2:                 $this->Country = $this->getFlagStr ( $this->EndIpOff+4) ;                 //echo sprintf('EndIpOffset=(%x)',$this->EndIpOff );                 $this->Local = ( 1 == $this->CountryFlag )? '' : $this->getFlagStr ( $this->EndIpOff+8);                 break ;             default :                 $this->Country = $this->getFlagStr ($this->EndIpOff+4) ;                 $this->Local =   $this->getFlagStr ( ftell ( $this->fp )) ;         }     }     function getFlagStr ( $offset )     {         $flag = 0 ;         while ( 1 ){             @fseek ( $this->fp , $offset , SEEK_SET ) ;             $flag = ord ( fgetc ( $this->fp ) ) ;             if ( $flag == 1 || $flag == 2 ) {                 $buf = fread ($this->fp , 3 ) ;                 if ($flag == 2 ){                     $this->CountryFlag = 2 ;                     $this->EndIpOff = $offset - 4 ;                 }                 $offset = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])* 256*256);             }else{                 break ;             }         }         if ( $offset < 12 )             return '';         @fseek($this->fp , $offset , SEEK_SET ) ;         return $this->getStr();     }     function getStr ( )     {         $str = '' ;         while ( 1 ) {             $c = fgetc ( $this->fp ) ;             if ( ord ( $c[0] ) == 0  )                break ;             $str .= $c ;         }         return $str ;     }     function qqwry ($dotip) {         $nRet;         $ip = IpToInt ( $dotip ); 
        $this->fp= @fopen(QQWRY, "r");         if ($this->fp == NULL) {               $this->Country = "Open File ".QQWRY." Error";               return 1;           }           @fseek ( $this->fp , 0 , SEEK_SET ) ;         $buf = fread ( $this->fp , 8 ) ;         $this->FirstStartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);         $this->LastStartIp  = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])*256*256) + (ord($buf[7])*256*256*256);         $RecordCount= floor( ( $this->LastStartIp - $this->FirstStartIp ) / 7);         if ($RecordCount <= 1){             $this->Country = "FileDataError";             fclose ( $this->fp ) ;             return 2 ;         }           $RangB= 0;         $RangE= $RecordCount;         // Match ...         while ($RangB < $RangE-1)         {           $RecNo= floor(($RangB + $RangE) / 2);           $this->getStartIp ( $RecNo ) ;             if ( $ip == $this->StartIp )             {                 $RangB = $RecNo ;                 break ;             }           if ( $ip > $this->StartIp)             $RangB= $RecNo;           else             $RangE= $RecNo;         }         $this->getStartIp ( $RangB ) ;         $this->getEndIp ( ) ;         if ( ( $this->StartIp  <= $ip ) && ( $this->EndIp >= $ip ) ){             $nRet = 0 ;             $this->getCountry ( ) ;             //这样不太好..............所以..........             $this->Local = str_replace("(我们一定要解放台湾!!!)", "", $this->Local); 
$this->Local = str_replace("CZ88.NET", "", $this->Local);
        }else {             $nRet = 3 ;             $this->Country = '未知' ;             $this->Local = '' ;         }         fclose ( $this->fp ) ;         return $nRet ;     } } function ip2location ( $ip ) {     $wry = new TQQwry ;     $nRet = $wry->qqwry ( $ip );     //可以利用 $nRet做一些事情,我是让他自动记录未知IP到一个表,代码就不写了。    return $wry->Country.$wry->Local ; } ?>

解决方案 »

  1.   

    我把 QQWry.dat 导进 MySQL 数据库,用数据库查询的方式,结果发现,原来的方式慢太多了...
    现在我又用回以上代码了:(
      

  2.   

    你把IP转成INT形就会快多了。
      

  3.   

    我有转换过了,可能是那个表记录数太多了.
    附转换程序如下,数据库需要有 FILE 权限:<?
    /*BY: [email protected]从 纯真网络:http://www.cz88.net 下载的IP数据,
    用自带的软件解压为 qqwry.txt 后放在本目录,
    然后导入 MySQL 数据库表的结构:
    CREATE TABLE `qqwry2` (
      `qqwryid` int(11) unsigned NOT NULL auto_increment,
      `ip1` int(10) unsigned NOT NULL default '0',
      `ip2` int(10) unsigned NOT NULL default '0',
      `address` varchar(255) NOT NULL default '',
      PRIMARY KEY  (`qqwryid`)
    ) TYPE=MyISAM;*/$dbhost = 'localhost';    // 数据库服务器
    $dbuser = 'root';    // 数据库用户名
    $dbpw = '';    // 数据库密码
    $dbname = 'iplocation'; // 数据库名$dbtable = 'qqwry2'; //ip数据表 1if(!mysql_connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect) or !mysql_select_db($dbname)){
    die("datebase error.");
    }
    set_time_limit(0);$fd = fopen ("qqwry.txt", "r");
    $temp_filename = tempnam( "/tmp", "ip_" );
    $fd2 = fopen ("$temp_filename", "w");$id = 0;while (!feof ($fd)) {
    $id++;
        $linestr = fgets($fd, 4096);
    if(!trim($linestr)) break;
        $ip1_str = trim(substr($linestr,0,15));
    $ip2_str = trim(substr($linestr,16,15));
    $address_str = trim(substr($linestr,32));

    $ip_arr = explode(".",$ip1_str);
    $ip1_str = 0;
    foreach($ip_arr as $i=>$s){
    $ip1_str += $s*pow(256,3-$i);
    } $ip_arr = explode(".",$ip2_str);
    $ip2_str = 0;
    foreach($ip_arr as $i=>$s){
    $ip2_str += $s*pow(256,3-$i);
    }
    fwrite($fd2,($id>1?"\r\n":"")."\"$id\",\"$ip1_str\",\"$ip2_str\",\"$address_str\"");
    }mysql_query("TRUNCATE TABLE `$dbtable`;");
    mysql_query("LOAD DATA LOCAL INFILE '".addslashes($temp_filename)."' INTO TABLE `$dbtable` FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\\\' LINES TERMINATED BY '\\r\\n'");
    fclose ($fd);
    fclose ($fd2);
    @unlink($temp_filename);die("导入完成.");?>
      

  4.   

    转一段纯真论坛的回帖,是发布qqwry数据库作者“金狐”的原话==================================
    数据库大并不代表是负担,也不会消耗更多的资源。数据库的数据是线性排列的,读取定位数据是采用二分法查找,数据库现在的26w个数据,在最坏情况下,只需要大概18次的比较/跳转操作就能定位,而你说的别的数据库大概5w数据,在最坏情况下大概需要15-16次比较/跳转,不可能出现过多的负担。
    当然,要是你的程序是采取直线读取/比较来判断的,则需要比较几十万次,那是程序员不懂或者不负责任造成的无必要消耗。这个和数据库无关。 
    ================================== 
    直接用二分法读取数据,速度很快的。
      

  5.   

    不用你做。意思是mysql查找数据采用的是二分法,你只要做好合适的索引即可。