为什么PHP和JavaScript在处理八进制和十六进制数时有问题?



我注意到PHP和JavaScript在处理八进制和十六进制数时遇到一些困难,而类型杂耍和类型转换:

PHP:

echo 16 == '0x10' ? 'true' : 'false'; //true, as expected
echo 8  == '010'  ? 'true' : 'false'; //false, o_O
echo (int)'0x10';    //0, o_O
echo intval('0x10'); //0, o_O
echo (int)'010';     //10, o_O
echo intval('010');  //10, o_O
JavaScript:

console.log(16 == '0x10' ? 'true' : 'false'); //true, as expected
console.log(8  == '010'  ? 'true' : 'false'); //false, o_O
console.log(parseInt('0x10')); //16, as expected
console.log(parseInt('010'));  //8, as expected
console.log(Number('0x10'));   //16, as expected
console.log(Number('010'));    //10, o_O

我知道PHP有octdec()hexdec()函数来纠正八进制/十六进制错误行为,但我希望intval()处理八进制和十六进制数字,就像JavaScript的parseInt()一样。

无论如何,这种奇怪行为背后的基本原理是什么?

想象某人指定035作为购买某些产品的数量(前面的0只是用于填充,因此它与列表中的其他三位数数量相匹配)。对于非程序员来说,035显然应该像35一样被解释。但是如果PHP在字符串中解释八进制数,结果会突然变成29 => WTF?!?另一方面,十六进制表示法的问题较小,因为人们通常不使用0x23表示法来指定数字。

顺便说一下,这不仅发生在最终用户身上,也发生在程序员身上。程序员经常试图用前导零填充他们的数字,嗯,一切都是错的!这就是为什么JS不再允许严格模式下的八进制表示法,而其他语言使用更显式的0o前缀。 顺便说一下,我同意这种行为是不一致的。在我看来,十六进制符号也不应该被解析。就像八进制和二进制表示法一样。特别是考虑到显式(int)强制转换也不解析十六进制,而只是读取直到第一个非数字的所有内容。

解决intval的情况,它的行为实际上就像文档中的一样:intval不是用于解析PHP的本机整数符号,而是用于解析指定基数的整数。如果你看一下文档,你会发现它接受第二个参数$base,默认为10。(顺便说一下,(int)强制转换在内部映射到与base = 10相同的convert_to_long_base调用,因此它的行为始终与intval完全相同。)

在javascript中,只有十进制和十六进制被定义为标准的一部分,而八进制是依赖于实现的,这就解释了为什么八进制解析在你给出的示例之间不一致。

你可以在严格模式下摆脱八进制文字,但在我测试的所有浏览器中,parseInt仍然试图解析八进制而不是十进制。这有点奇怪,因为规范没有说任何关于试图解释parseInt的隐含八进制的事情,并且明确禁止在严格模式下进行八进制扩展。所以没有八进制字面量,规范中没有关于在parseInt 'd时将"010"转换为八进制的内容,并且即使在严格模式下这种行为仍然存在。

所以Number("012") === 12是正确的,而parseInt("012") === 10是不正确的,根据我对规范的解释,你可以在这里阅读

使用十六进制有一个很好的理由,它使位级别的数字操作更容易。如果不是十六进制的话,"0xFF"是不会出现的。

没有阅读其他答案,但至少在PHP中,八进制或十六进制数没有问题;你做错了

"0x12" // String with content "0x12"
0x12 // Integer "18"
010 // integer "8"

将字符串转换为整数将…是的,像PHP那样将其转换为整数:它将接受任何数字并从中形成整数,直到找到任何非数字字符。本例中只有0

hexdec()适用于字符串,但该字符串仅为十六进制,不带前缀0x

echo hexdec('A0`); // 16

前缀0(八进制)和0x(十六进制)是为了区分不同的整数符号而存在的,但是只要你把它写成字符串,PHP就会把它当作字符串来处理。

最新更新