Skip to content

IP地址转化为数值的方案

cnwhy edited this page Sep 18, 2013 · 2 revisions

关于IP地址转化为数字我测试了几种方案:

首先是IP地址的验证,我选择了用正则表达式:

var REG =/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;

后面用到REG就是这个了,虽然这个正则有点长,但用它可以把验证和分割一步到位,很方便;

方案1:每8位转成16进制字符,拼接后转成整数

function ipToInt(IP){
		var xH = "",result = REG.exec(ip);
		if(!result) return -1;
		for (var i = 1; i <= 4; i++) {
			var h = parseInt(result[i]);
			xH += (h > 15 ? "" : "0") + h.toString(16);
		}
		return parseInt(xH, 16);
	}

如果不要验证的话就可以换一个帅气一点的写法:

	return parseInt(IP.replace(/\d+\.?/ig,function(a){
		a = parseInt(a); 
		return (a > 15 ? "" : "0") + a.toString(16);
	}),16);

方案2:直接计算

	function ipToInt(IP){
		var xH = "",result = REG.exec(ip);
		if(!result) return -1;
		return (parseInt(result[1]) * 0x1000000 
			+ parseInt(result[2]) * 0x10000 
			+ parseInt(result[3]) * 0x100 
			+ parseInt(result[4]));
	}

方案3:按位计算

我一想,直接记算还不如安位算于是改了一下:

	function ipToInt(IP){
		var xH = "",result = REG.exec(ip);
		if(!result) return -1;
		return (parseInt(result[1]) << 24 
			| parseInt(result[2]) << 16
			| parseInt(result[3]) << 8
			| parseInt(result[4]));
	}

上面按位运算看起来没什么问题,试了几个IP地址之后就发现不对了
当IP的值大于0x7f000000时变负数了,我了个去JS把它当成32位的整型,最左边一位成了符号位
本打算放弃的时候一想,不对啊,符号位不也就是0,1吗,JS还有无符号右移运算符呢(>>>)
于是上面的函数改良之后就成了这样:

	function ipToInt(IP){
		var xH = "",result = REG.exec(ip);
		if(!result) return -1;
		return (parseInt(result[1]) << 24 
			| parseInt(result[2]) << 16
			| parseInt(result[3]) << 8
			| parseInt(result[4]))>>>0;
	}

测试之后果然OK了。

方案4:按位,字符串拼接结合

	function ipToInt(IP) {
		var xH = "",
		result = REG.exec(ip);
		if (!result) return - 1;
		var ip2 = "000000" + ((parseInt(result[2]) << 16) | (parseInt(result[3]) << 8) | parseInt(result[4])).toString(16);
		return parseInt(parseInt(result[1]).toString(16) + ip2.substr(ip2.length - 6), 16);
	}

你肯定在问为什么会有这么怪的想法,其实是安位运算出现了符号位的问题不甘心放弃,这个想法就这么出来了.

总结

照道理说"方案3"因该是最有效率的方法.
但最后100万次的运算测试表明"方案2"的效率和"方案3"相差无几,难道是因为V8?
虽然"方案2"的代码更容易理解,但最后抱着对算法的敬畏还是选择了"方案3";