关于ip地址存储类型的探讨

2014年03月24日 10:00 by:Tsung

导读: 只要是用到网络的地方就避免不了涉及到ip地址, php和mysql中处理ip地址的方法有很多, 有一部分人习惯以char(15)的数据类型来存储ip地址, 更多的网友把ip地址用ip2long转换成整形存储, 整形存储的大小和读取速度肯定优于字符串, php默认的字符串转换函数因为系统不同会产生负数以及超过int长度的问题. 针对这个问题老修综合网上的资料来分享一下.

关于ip地址存储的方法, ip地址很多人都用char方式存储, 但我建议还是觉得用整数来存储读取更快. 

ip地址转整数的方式比较多, 我逐一说明一下, 部分代码参考网友:Tsung的文章.

ip地址查询请访问: http://www.lao8.org/chk_ip.php

1. 用ip2long ip地址转整数

ip2long() 转出来的数值在32位机器转出负数, 系统版本 32bits 和 64bits 转换的结果不一样. 

  1. 32 bits ip2long(): -2147483648 ~ 214748364764
  2. 64 bits ip2long(): 0 ~ 4294967295

32bits 系统测试ip2long() 

  1. ip2long('127.255.255.255'); // 2147483647 = 十进位的最大值
  2. ip2long('255.255.255.255'); // -1
  3. ip2long('255.255.255.254'); // -2
  4. ip2long('192.168.1.2'); // -1062731518

64bits 系统测试ip2long()

  1. ip2long('127.255.255.255'); // 2147483647 = 十进位的最大值
  2. ip2long('255.255.255.255'); // 4294967295
  3. ip2long('255.255.255.254'); // 4294967294
  4. ip2long('192.168.1.2'); // 3232235778

知道问题是 32bits 系统造成的, 就很好解决萝~

方法1. ip2long 转成二进位, 再转回十进位避免负值

  1. <?php
  2. echo bindec(decbin(ip2long('192.168.1.2'))); // 3232235778
  3. ?>
老修备注一下, 这个方法ipv4和ipv6通吃, 数据存储格式要用Unsigned 无标签int类型.

方法2. 可避免ipv4转换后产生负数的另一种算法:

  1. <?php
  2. // 自己做转换, 这方法计算出来的数值是正确的.(32bits / 64bits 皆正确)
  3. function iptolong($ip)
  4. {
  5. list($a, $b, $c, $d) = split('.', $ip);
  6. $ip_long = (($a * 256 + $b) * 256 + $c) * 256 + $d;
  7. return $ip_long;//如果加上intval($ip_long);会产生负数
  8. }
  9. ?>

此方法同样有的ip地址会产生bigint值.

注: intval 在 32 bits / 64 bits 最大值是不同的:

The maximum value depends on the system.
32 bit systems have a maximum signed integer range of -2147483648 to 2147483647.
So for example on such a system, intval('1000000000000') will return 2147483647.
The maximum signed integer value for 64 bit systems is 9223372036854775807.

方法3. 使用 printf("%u") 

  1. <?php
  2. $ipbigint = sprintf("%u", ip2long('192.168.1.2'));
  3. ?>

这里要注意的是printf 和sprintf的区别, printf是直接输出给浏览器了, 相当于echo (sprintf());

方法4. 使用mysql的 inet_aton 转换函数.

我不建议在mysql 的查询中做数据运算, 这会给脆弱的数据库造成额外的压力, 能php解决的尽量不要放到数据库里操作. 

不过方法还是分享一下:

  1. select * from iplist where inet_aton(ip)=inet_aton('{$ip}')