当字符串为十六进制,前缀为';0x';


inconsistency in converting string to integer, when string is hex, prefixed with '0x'

使用PHP 5.3.5。不确定这在其他版本上是如何工作的。

我对使用包含数字的字符串感到困惑,例如'0x4B0''1.2e3'。PHP处理这些字符串的方式在我看来似乎不一致。难道只有我一个人吗?还是一个bug?或者未记录的功能?还是我只是在文档中漏掉了一些神奇的句子?

<?php
echo $str = '0x4B0', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1           -> ", var_dump($str * 1);         // int(1200)
echo "(int)        -> ", var_dump((int)$str);        // int(0)
echo "(float)      -> ", var_dump((float)$str);      // float(0)
echo PHP_EOL;
echo $str = '1.2e3', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1           -> ", var_dump($str * 1);         // float(1200)
echo "(int)        -> ", var_dump((int)$str);        // int(1)
echo "(float)      -> ", var_dump((float)$str);      // float(1200)
echo PHP_EOL;

在这两种情况下,is_numeric()都返回true。此外,在这两种情况下,$str * 1都解析字符串并返回有效数字(一种情况下为整数,另一种情况为浮点)。

(int)$str(float)$str铸造会产生意想不到的结果。

  • (int)$str在任何情况下都只能解析数字,前面有可选的"+"或"-"
  • (float)$str更高级,可以解析类似^[+-]?'d*('.'d*)?(e[+-]?'d*)?的东西,即可选的"+"或"-",后面跟着可选数字,后面跟着带可选数字的可选小数点,后面跟着由"e"和可选的"+"或"-"组成的可选指数,后面跟着选择数字。但是在十六进制数据上失败

相关文档:

  • is_numeric()-声明"也允许使用十六进制表示法(0xFF),但只允许不包含符号、小数和指数部分"。若用于测试字符串是否包含数字数据的函数返回true,我希望PHP能够将这样的字符串转换为数字。这似乎适用于$str * 1,但不适用于选角。为什么
  • 转换为integer-表示"在大多数情况下不需要强制转换,因为如果运算符、函数或控制结构需要整数参数,则会自动转换值"。在这样的语句之后,我希望$s * 10(int)$s * 10表达式以相同的方式工作,并返回相同的结果。不过,如示例所示,这些表达式的求值方式不同
  • 字符串转换为数字-表示"有效的数字数据是一个可选符号,后跟一个或多个数字(可选包含小数点),后跟可选指数"。"Exponent"是"e"或"e",后面跟数字,例如1.2e3是有效的数字数据。未提及符号("+"或"-")。它没有提到十六进制值。这与is_numeric()中使用的"数字数据"的定义相冲突。然后,建议"有关此转换的更多信息,请参阅strtod(3)的Unix手册页",man strtod描述了其他数值(包括HEX表示法)。那么,在阅读了这篇文章之后,十六进制数据应该是有效的还是无效的数字数据

所以。。。

  • 当字符串用作数字时,is_numeric()和PHP处理字符串的方式之间是否存在(或者应该存在)任何关系
  • $s0x4B0还是1.2e3时,为什么(int)$s(float)$s$s * 1的工作方式不同,即给出完全不同的结果
  • 如果字符串被写成0x4B01.2e3,有没有办法将其转换为数字并保持其值?floatval()根本不适用于HEX,intval()需要将$base设置为16才能使用HEX,使用(int)$str(float)$str的类型转换有时有效,有时不起作用,因此这些都不是有效的选项。我也不考虑$n *= 1;,因为它看起来更像是数据操作,而不是转换。在这种情况下也不考虑自写函数,因为我正在寻找本机解决方案

直接转换(int)$str(float)$str的工作方式并没有什么不同:它们都可以从字符串中读取尽可能多的字符,并将其解释为各自类型的数字。

对于"0x4B0",int转换读取"0"(OK),然后读取"x"并停止,因为它无法将"x"转换为整数。浮点转换也是如此。

对于"1.2e3",int转换读取"1"(OK),然后读取"."并停止。浮点转换将整个字符串识别为有效的浮点表示法。

$str * 1这样的表达式的自动类型识别比显式类型转换更灵活。显式强制转换本质上要求整数和浮点为printf%i%f生成的格式。

不过,也许您可以使用intval和floatval,而不是显式转换为int,以获得更大的灵活性。

最后,您的问题"十六进制数据应该是有效的还是无效的数字数据?"很尴尬。没有所谓的"十六进制数据"。十六进制只是一个基数可以使用类似"4B0"的字符串,并使用strtoul等将其解析为任何2到36之间的数字基数中的整数。[对不起,那是BS。PHP中没有strtoul。但intval具有等效的功能,请参阅上文。]

intval使用strtol,当base参数为零时,它可以识别oct/hex前缀,因此

var_dump(intval('0xef'));     // int(0)
var_dump(intval('0xff', 0));  // int(255)

Is_numeric()和PHP将字符串用作数字时处理字符串的方式之间是否存在(或者更确切地说,是否应该存在)任何关系?

PHP中没有名为numeric的数据类型,is_numeric()函数更像是对PHP可以解释为数字的东西的测试。

就这种数字解释而言,在值前面添加+实际上会使PHP将其转换为数字:

$int = +'0x4B0';
$float = +'1.2e3';

您可以在字符串手册中找到解释,请参阅字符串转换为数字一节。

由于它是由一个运算符触发的,所以我认为PHP中不需要有一个函数来做同样的事情。这将是多余的。


PHP内部使用一个名为zendi_convert_scalar_to_number的函数作为加法运算符(假定为+),该运算符将使用is_numeric_string来获取数字。

当与字符串一起使用时,is_numeric()会在内部调用完全相同的函数。

因此,要触发本机转换函数,我只需要使用+运算符。这将确保您将返回数字伪类型(int或float)。

参考:/Zend/Zend_operators.c/ext/standard/type.c