var _parse = function (ip) {
var octets = ip.split('.');
return octets.map(function (octet) {
return octet | 0;
});
},
_isIPv4 = function (ip) {
var octets = _parse(ip),
len = octets.length,
isValid = true;
if (len !== 4) return false;
octets.forEach(function (octet) {
if (octet < 0 || octet > 255) {
isValid = false;
}
});
return isValid;
},
toLong = function (ip) {
var octets = _parse(ip);
if (!_isIPv4(ip)) {
throw 'Invalid IPv4 address!';
}
return ((octets[0] << 24) >>> 0) +
((octets[1] << 16) >>> 0) +
((octets[2] << 8) >>> 0) +
(octets[3]);
};
#1 楼
我不会在“内部”函数前加上下划线。相反,只需将其放在模块中,然后仅导出要导出的函数即可。如果您关心遵循节点样式指南,则应该知道该节点建议忽略Crockford的恋物癖一个
var
声明。注意:这是有争议的。我不喜欢顶部的
var
声明,尤其是当它们都塞满一个长且连续的声明时。但是,成千上万的人会为此而奋斗。您的验证规则不够严格,除非您只关心存在三个时期。
...
,a.b.c.d
只是两个简单的示例,它们显然是完全无效的,但是您的代码允许。简而言之,对于此类情况,隐式转换是邪恶的。隐式
0
只是掩盖了错误,而不是让您知道它发生了。不用显式转换为数字,而是使用parseInt()
来验证您是否没有返回NaN
:var i = parseInt(s, 10); if (Number.isNaN(i)) { /* uh oh! */ }
。不要抛出字符串。而是抛出某种异常对象(例如从
Error
派生的异常对象)。toLong
是一个非常非常模糊的名称。再一次,在一个模块中这会很好,但是作为一个简单的功能,它使我想知道它实际上在做什么。 (而且,JavaScript中不存在long
,因此有点用词不当)。零填充移位运算符在这里什么也不做,因为可以保证数字
编辑:显然,JS会将左移的数字解释为带符号(可疑的决定,但是...)。这意味着,当
x << 24
在128到255之间时,x
将为负。简而言之,>>> 0
实际上正在做24位移的事情。尽管如此,在其他步骤上,它实际上什么也没做。octets.forEach
应该实际上是使用every
代替的:return octets.every(function(v) { return v >= 0 && v <= 255; });
考虑到所有这些功能错配的相互作用,我可能只会坚持使用一个功能。是的,最好单独处理验证,但是我担心时间到了,如果到了,那么您总是可以将其分为两个功能。
function ipStringToLong(ip) {
var octets = ip.split('.');
if (octets.length !== 4) {
throw new Error("Invalid format -- expecting a.b.c.d");
}
var ip = 0;
for (var i = 0; i < octets.length; ++i) {
var octet = parseInt(octets[i], 10);
if (Number.isNaN(octet) || octet < 0 || octet > 255) {
throw new Error("Each octet must be between 0 and 255");
}
ip |= octet << ((octets.length - i) * 8);
}
return ip;
}
(我无法决定我对
ip |=
的感觉如何……可能更干净一些,可以像代码中那样添加附加内容。)#2 楼
尽管不需要>>>
右移,但按位运算很好。 @Lucien和@Corbin是正确的!右移运算符可防止(128 << 24)
解释为负数。其余代码也不错,但对我来说有点长。就个人而言,我将使用一个长的正则表达式来进行所有的解析和验证。这将负责
.split()
,length == 4
检查,.forEach()
和.map()
循环以及0≤八位位组≤255的检查。var ipv4QuadToLong = (function() {
var octet = '([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])';
var ipRegExp = new RegExp('^' + octet + '\.' + octet + '\.' + octet + '\.' + octet + '$');
return function ipv4QuadToLong(string) {
var octets = string.match(ipRegExp);
if (octets === null) {
throw 'Invalid IPv4 address!';
} else {
// @Lucien and @Corbin are right! The right-shift operators
// prevent (128 << 24) from being interpreted as a negative
// number.
return ((octets[1] << 24) >>> 0) +
((octets[2] << 16) >>> 0) +
((octets[3] << 8) >>> 0) +
(octets[4] << 0);
}
};
})();
评论
\ $ \ begingroup \ $
我避免使用正则表达式,因为我在正则表达式中担任忍者的能力有限,哈哈。在编写测试时,我试图首先瞄准最简单的方法,然后根据需要重新进行测试。我很高兴您指出了这一点。我需要在正则表达式中加分!
\ $ \ endgroup \ $
–user27606
2014年6月20日,下午3:13
\ $ \ begingroup \ $
IMO由于操作繁琐,因此在此类功能中使用RegExp是个坏主意。
\ $ \ endgroup \ $
–罗马·波德利诺夫
15年11月26日在9:56
#3 楼
您对IPv4地址的解析是不正确的,因为它成功解析了RFC 810中指定的点分四进制表示的IPv4地址。但是,您可能想知道Unix中的inet_aton()
函数更加宽松,支持某些非常规形式: 1 : 0. 0. 0. 1
127.1 : 127. 0. 0. 1
192.168.1 : 192.168. 0. 1
8.8.8.8 : 8. 8. 8. 8
您应该准备好接受1到4个八位字节(包括8个字节),并将它们固定在适当的位置。虽然大多数人使用格式完整的IP地址,但可能是懒惰的用户可能尝试使用巧妙的快捷方式并最终导致意外错误。
在IPv6中,使用以下语法也允许使用此语法: :语法,因此您至少应该意识到快捷键确实存在的事实。例如,您可以在现代计算机上执行此操作:
ping ::1
(localhost)。这是我处理1至3个八位字节的方法:
var _parse = function (ip) {
var octets = ip.split('.');
while(octets.length < 4) {
octets.splice(0, octets.length-1, 0);
}
return octets.map(function (octet) {
return octet | 0;
});
},
评论
\ $ \ begingroup \ $
我不知道。您如何建议处理此问题?
\ $ \ endgroup \ $
–user27606
2014年6月20日,3:20
\ $ \ begingroup \ $
@LucienLachance只需添加一个简单的填充循环,如我更新后的答案所示。
\ $ \ endgroup \ $
– phyrfox
2014年6月20日,下午3:41
\ $ \ begingroup \ $
哦,我明白了!这将是一个很好的功能。我非常喜欢这个主意。
\ $ \ endgroup \ $
–user27606
2014年6月20日下午3:55
\ $ \ begingroup \ $
请引用?我不认为“ 127.1”是常规或合法的。 RFC 810定义 :: =
\ $ \ endgroup \ $
– 200_success
2014年6月20日下午5:18
\ $ \ begingroup \ $
@ 200_success回想起来,这个答案来自我对IPv4的错误理解,这是基于我发现这一情况的情况。我确定这是一个小故障,因为我长大了4个八位位组形式,并认为其他任何错误。看来我的第一印象是正确的。一些RFC资料错误地允许1-4个八位位组,在此Internet草案中概述:tools.ietf.org/html/draft-main-ipaddr-text-rep-02。感谢您鼓励对此主题进行进一步的研究,因为我现在知道它们为什么起作用,以及我们为什么不在乎它们起作用。
\ $ \ endgroup \ $
– phyrfox
2014年6月20日下午5:44
评论
\ $ \ begingroup \ $
很好。我试图将事物分开,只是因为我习惯于将事物保持模块化,但是我喜欢这里的想法。我从来都不擅长使用“每个”,在这种情况下,这样做会更好。您能否解释一下为什么我应该改用parseInt?好的文章。
\ $ \ endgroup \ $
–user27606
2014年6月20日的3:08
\ $ \ begingroup \ $
还有一件事情,当给出'172.2.6.139'时,它会返回-1409284469。返回IP >>> 0似乎可以解决问题。
\ $ \ endgroup \ $
–user27606
2014年6月20日4:40
\ $ \ begingroup \ $
@LucienLachance啊,我应该已经看到了。最高字节上的>>> 0实际上正在做某事。基本上发生的是172的符号位高(因为172&128!= 0),这意味着JS将172 << 24视为负数(在我看来,这很奇怪-但是,嘿,它是JS,我们不能对理智的操作寄予太大希望)。无论如何,简单的解决方法是只返回ip >>> 0而不是返回ip。这使它意识到该数字是无符号的。
\ $ \ endgroup \ $
– Corbin
2014年6月20日下午4:46
\ $ \ begingroup \ $
不错的答案,尽管当循环使用octets.length时,我不喜欢硬编码(3-i)。我知道长度已检查,但仍然...让我有些不适。也许是for(var i = 0,l = array.length-1; i <= l; i ++)然后是(l-i)*8。或者您可以在循环之前反转数组,只用i * 8
\ $ \ endgroup \ $
– Flaambino
2014年6月20日14:25
\ $ \ begingroup \ $
@Flambino Wow ....我考虑了向后迭代,但是我不敢相信我使用了硬编码3。我为自己没被抓住而感到尴尬。谢谢!
\ $ \ endgroup \ $
– Corbin
14年6月20日在21:35