为什么 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()
确实如此。
无论如何,这种奇怪行为背后的理由是什么?
I have noticed that PHP and JavaScript treat octal and hexadecimal numbers with some difficulty while type juggling and casting:
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
I know that PHP has the octdec()
and hexdec()
functions to remedy the octal/hexadecimal misbehaviour, but I'd expect the intval()
to deal with octal and hexadecimal numbers just as JavaScript's parseInt()
does.
Anyway, what is the rationale behind this odd behaviour?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
想象一下,有人指定
035
作为某种产品购买的数量(前导0
仅用于填充,以便与列表中的其他三位数数量匹配)。对于非程序员来说,035
显然应该像35
一样被解释。但是如果 PHP 解释字符串中的八进制数字,结果会突然变成29
=>搞什么鬼?!?另一方面,十六进制表示法的问题较小,因为人们通常不使用0x23
表示法来指定数字。顺便说一句,这种情况不仅发生在最终用户身上,也发生在程序员身上。程序员经常尝试用前导零填充数字,然后 - 嗯,一切都错了!这就是为什么 JS 不再允许严格模式下的八进制表示法,而其他语言则使用更明确的
0o
前缀。顺便说一句,我确实同意这种行为是不一致的。在我看来,十六进制表示法也不应该被解析。就像八进制和二进制表示法不是一样。特别是考虑到显式
(int)
转换也不解析十六进制,而是仅读取第一个非数字之前的所有内容。针对
intval
的情况,它实际上的行为就像记录的那样:intval
不是用于解析 PHP 的本机整数表示法,它用于解析指定基数的整数。如果您查看文档,您会发现它需要第二个参数$base< /code> 默认为
10
。 ((int)
顺便在内部映射到相同的convert_to_long_base
使用base = 10
调用,因此它的行为始终与intval
。)Imagine somebody specifies
035
as a quantity for some product to buy (the leading0
is just for padding so it matches other three-digit quantities in the list).035
is obviously expected to be interpreted just like35
for a non-programmer. But if PHP were to interpret octal numbers in strings the result would suddenly be29
=> WTF?!? Hexadecimal notation on the other hand is less of a problem because people don't commonly specify numbers using a0x23
notation.This by the way doesn't only happen to end users, but to programmers too. Often programmers try to pad their numbers with leading zeros and - huh, everything is wrong! That's why JS doesn't allow octal notation in strict mode anymore and other languages use the more explicit
0o
prefix.By the way, I do agree that this behavior is inconsistent. In my eyes hexadecimal notation shouldn't be parsed either. Just like octal and binary notation is not. Especially considering that the explicit
(int)
cast doesn't parse hex either and instead just reads everything up to the first non-digit.Addressing the
intval
case, it actually behaves just like documented:intval
isn't there for parsing PHP's native integer notations, it is for parsing integers of a specified base. If you have a look at the docs, you'll find that it takes a second argument$base
which defaults to10
. (The(int)
cast by the way internally maps down to the sameconvert_to_long_base
call withbase = 10
, so it will always behave exactly likeintval
.)在javascript中,只有十进制和十六进制被定义为标准的一部分,而八进制是依赖于实现的,这可以解释为什么八进制解析在您给出的示例之间不一致。
您可以在严格模式下摆脱八进制文字,但在我测试的所有浏览器中,parseInt 仍然尝试解析八进制而不是十进制。这有点奇怪,因为规范没有说明如何尝试解释 parseInt 的隐含八进制,并且在严格模式下明确禁止八进制扩展。因此,没有八进制文字,规范中没有任何关于在解析 Int 时尝试将
"010"
转换为八进制的内容,并且即使在严格模式下,该行为仍然存在。因此,根据我对规范的解释,
Number("012") === 12
是正确的,而parseInt("012") === 10
是不正确的可以阅读此处有一个很好的理由虽然是十六进制,但它使位级数字的操作变得更加容易。如果某人不表示十六进制,则不会输入“0xFF”。
In javascript, only decimal and hex are defined as part of the standard, while the octal is implementation dependent, which would explain why octal parsing is not consistent between the examples you gave.
You can get rid of octal literals in strict mode but in all browsers I tested,
parseInt
still tried to parse an octal instead of decimal. Which is kind of strange because the spec does not say anything about trying to interpret implied octal forparseInt
and explicitly prohibits the octal extension when in strict mode. So no octal literals, nothing in the spec about trying to turn"010"
into an octal whenparseInt
'd, and the behavior persists even in strict mode.So
Number("012") === 12
is correct whileparseInt("012") === 10
is not correct according to my interpretations of the spec which you can read hereThere is a good reason for hexadecimal though, it makes operations on numbers at bit level much easier. And "0xFF" is not something someone types if he doesn't mean a hex.
没有阅读其他答案,但至少在 PHP 中八进制或十六进制数字没有问题;你只是做错了
将字符串转换为整数将...是的,将其转换为整数,就像 PHP 总是这样做的那样:它将采用任何数字并从中形成整数,直到找到任何非数字字符。在这种情况下,它唯一的
0
hexdec()
适用于字符串,但该字符串仅是没有前缀0x
的十六进制。前缀
0
(八进制)和0x
(十六进制)的存在是为了区分不同的整数表示法,但只要你将其写为字符串,PHP 就会将其视为它作为一个字符串。我猜你在 javascript 上也犯过类似的错误。
Didn't read the other answer, but at least in PHP there is no problem with octal or hexadecimal numbers; you just doing it wrong
Casting the string to integer will ... yes, cast it to integer the way PHP always does it: It will take any number and form the integer out of it until it founds any non-numeric character. In this case its only
0
hexdec()
works on strings, but this strings are hexadecimal only without the prefix0x
.The prefixes
0
(octal) and0x
(hexadecimal) exists to distinguish the different integer notations from each other, but as long as you write it as a string, PHP will treat it as a string.I assume, that you did a similar mistake with javascript.