闲来无事,整理了平时用到的一些代码,顺便在github上发布了自己第一个开源项目,项目很简单,使用不同的办法来从纯真IP数据库中获取IP地址所对应的地理位置信息,同时还可以把纯真数据库里面记录的乱七八糟的位置信息转变成对应的国内的省份信息,以方便进行统计
- 通过PHP代码直接操作纯真数据库二进制文件来实现IP地理位置信息查找
- 通过PHP扩展qqwry操作纯真数据库二进制文件来实现IP地理位置信息查找
- 先把纯真数据库二进制文件转化成sqlite数据库,然后从sqlite数据库中查找IP所对应的地理位置信息
通过PHP操作纯真数据库的代码可以很容易从网上找到的,我这里的算法也是参照网上的代码实现的,具体代码可以到github上下载,项目地址:https://github.com/starfalling/php-IpLocationSeeker。纯真数据库的下载地址:http://update.cz88.net/soft/qqwry.rar,因转换之后的sqlite文件比较大,这里就不提供下载了,大家可以从前面的连接下载到最新的纯真数据库二进制文件,然后转换一下就可以了。
这三种不同情况下,性能最优的为qqwry扩展,毕竟,机器码的运行速度肯定是要超过动态语言的脚本的,也当超过sqlite去做sql分析然后执行的。不过sqlite版本的性能已经跟qqwry扩展比较接近,想要更好的性能又不方便自行编译php扩展的就完全可以用sqlite版本来进行替代了:
[binary] times:10000 2.53s used # 注意,这里只重复运行了1万次,而下面两个运行了十万次 [binary-e] times:100000 4.29s used # 扩展 + 二进制文件 [sqlite] times:100000 5.40s used # PHP + sqlite
这里再简单介绍一下转化之后的数据在sqlite中的存储格式
create table qqwry ( ip integer primary key, country varchar(255), area varchar(255) )
这个表总共有三个字段,主键ip存储了这条记录所对应的转化成为有符号整数之后的ip地址,在php里面,要把字符串形式的IP地址转化成整数非常简单,有自带函数ip2long;country字段存储了qqwry里面的一级地址信息,area存储了二级地址信息。
我们取其中的几条记录来进行分析:
sqlite> select * from qqwry limit 10; -2147483648|欧洲| # 128.0.0.0 -2147418112|美国| CZ88.NET # 128.1.0.0 -2147352576|美国|卡耐基·梅隆大学 # 128.2.0.0 -2147301276|美国|卡耐基·梅隆大学DNS服务器 -2147301275|美国|卡耐基·梅隆大学 -2147287040|美国|能源部劳伦斯伯克利国家实验室 -2147221504|美国|特拉华大学 -2147155968|美国| CZ88.NET -2147090432|美国|罗格斯大学 -2147024896|德国|夫琅和费应用研究促进学会
第一条记录所对应的IP地址为128.0.0.0,第二条记录对应的IP地址为128.1.0.0,根据qqwry存储规则,这两条记录之间的所有IP地址的地理位置信息都按第一条处理,也即128.0.1.1这个IP地址所对应的地理位置为欧洲。这样我们在查询一个IP地址所对应的地理位置就很简单了,如代码里面所写到的,只要用一个sql查询就既可以解决了:
$sql = "select * from qqwry where ip<{$ip_int} order by ip desc limit 1";
使用时,转换sqlite的函数对于相当部分的记录会报错:Notice: Uninitialized string offset: 0,字符串越界,出错的语句是:if(ord($area{0}) == 2) $area = “”;
如果把此句注释,则后面的两句转码 iconv(“gb18030″, “utf-8″, …); 也会报错;
报错后,转换出来的sqlite数据库,有很多错误的记录,现象是country和area不匹配;
如果注释掉三句转换不报错,出来的数据库查询结果正确,但直接dat方式直接查询则结果错误且乱码,若不注释转码语句,dat方式不乱码但结果错误;
========
if(ord($area{0}) == 2) $area = “”; // 不规则字符
$country = iconv(“gb18030″, “utf-8″, $country);
$area = iconv(“gb18030″, “utf-8″, $area);
在测试查询直接echo输出查询结果(Chrome编码自动检测/utf-8设置)时:
以上三句注释后,中英混合结果(如:127.0.0.1 IANA保留地址)返回乱码(且SQLite3结果正确,DAT结果错误),如取消注释,则转换SQLite3过程中部分记录报错(导致查询结果错误),DAT查询结果不变但混合无乱码。
—-
包装成函数用于SQL写入数据库的时:
一切中文都会乱码并导致INSERT失败。
===========
请问这是什么原因,难道是QQwry.dat版本更新,数据结构有所改变的关系?(2012.10.30版)
由于不太清楚你注释中所说的“不规则字符”指什么,也不清楚dat里的某些记录到底有何特殊导致转码失败,所以未能自己解决。
实在不行,只能用查到的其他几个解决方案了,哎~盼复啊~Q: 365458655
新版的qqwry.dat,要把文件里的
$area = iconv(“gb18030″, “utf-8″, $area);
改成
$area = iconv(“gb2312″, “utf-8″, $area);
补充说明,iconv的报错信息是:Notice: iconv(): Detected an illegal character in input string 和 Notice: iconv(): Detected an incomplete multibyte character in input string
只能跟你说,程序开发,能不用windows就不用windows吧