比较字符串和整数会得到奇怪的结果。

40
我真的很困惑为什么这个操作有效。有人可以解释一下吗?
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";
$test2 = "3581169b064f71be1630b321d3ca318f";

if ($test1 == 0)
  echo "Test 1 is Equal!?";
if ($test2 == 0)
  echo "Test 2 is Equal!?";

// Returns: Test 1 is Equal!?

为了澄清,我正在尝试将字符串"0"$test变量进行比较。我已经知道修复代码的方法,只需将0""括起来即可(正如我本应该做的)。
我想知道这是PHP的错误、服务器的错误,还是某种有效的操作。根据https://www.php.net/types.comparisons,这不应该起作用。 编辑:算了,显然它确实提到了字符串和0之间的松散比较是真的。但我仍然不知道为什么。 编辑2:我修改了我的问题,为什么$test2的值"3581169b064f71be1630b321d3ca318f"不起作用?

哪个 $test 值不起作用? - Assaf Lavie
如果您运行此代码,它只会输出 Test 1 is Equal!? - St. John Johnson
2
编辑 2:由于比较语句自动将“3581169b064f71be1630b321d3ca318f”转换为整数,并将其作为值获取字符串的第一部分,因为它以数字开头。所以那个比较(3581169 == 0)是假的。 - Erdinç Çorbacı
值得一提的是,打印 $test1 + 0$test2 + 0 的值会提示发生了什么。或者,使用恒等运算符打印 +$test1+$test2 的值也可以。 - ToolmakerSteve
5个回答

44

来自PHP手册

将字符串转换为数字

当一个字符串在数值上下文中被评估时,其结果的值和类型如下所示。

如果字符串包含字符'.'、'e'或'E',则将其评估为浮点型。否则,它将被评估为整型。

该值由字符串的初始部分给出。如果字符串以有效的数值数据开头,则使用该值。否则,该值将是0(零)。有效的数值数据是可选符号,后跟一个或多个数字(可选包含小数点),后跟一个可选的指数。指数是'e'或'E'后跟一个或多个数字。


仍然认为它不应该转换为0,或者至少如果以数字开头且字符串中存在错误字符,则不应将其转换为小数。 - St. John Johnson
2
PHP没有像JavaScript(另一种弱类型语言)那样的“NaN”类型,所以它基本上只能转换为0,因为没有其他办法(转换为NULL不起作用,因为NULL也会转换为0)。我认为解决办法是不使用松散比较(==),或者先验证字符串。 - thomasrutter
1
除了这个答案之外,当使用==运算符比较两个元素时,它会被转换为通用类型。在这里提到了这一点。http://www.php.net/manual/en/language.operators.comparison.php - Nis
1
最好的测试方法是使用“empty”方法:empty($test1) - Pascal_dher
@Pascal_dher - 嗯,不行。原因:说清楚你的意思。在这里,意图是询问问题“这个值是否等于0”。一个看起来类似于意图的表达式是显而易见的,而依赖于语言怪癖的表达式则是不明显的。在我看来。[如果唯一存在的计算机语言是PHP,那么你的建议就是合理的。然而,这仍然不能使它成为最好的方法;它只能成为同样好的替代方案。] - ToolmakerSteve
请注意,这在 PHP 8 中已经发生了变化。如果在数字环境中使用非数字字符串,则会抛出“TypeError”。https://www.php.net/manual/en/language.types.numeric-strings.php - Barmar

12

使用 == 运算符进行类型转换

== 运算符是一种松散类型的比较。它会将两个值转换为一个公共类型并进行比较。字符串转换为整数的方式可以在这里解释

请注意,你链接到的页面并不与此相矛盾。请查看第二个表格,其中说明使用 == 将整数 0 与字符串 "php" 进行比较将返回 true。

实际上,字符串被转换为整数,而非数字字符串(不包含或以数字开头的字符串)会转换为 0

数字字符串与非数字字符串

由数字组成或以数字开头的字符串被视为数字字符串。如果该字符串后面还有其他字符,这些字符将被忽略。

如果一个字符串以一个不能被解释为数字的字符开头,那么它就是一个非数字字符串,并且会转换为0。这并不意味着一个数字字符串必须以数字(0-9)开头 - 例如,"-1"是一个数字字符串,因为减号在这种情况下是数字的一部分。
所以举个例子,你的字符串"d85d1d81b25614a3504a3d5601a9cb2e"不以数字开头,所以它会转换为0。但是你的第二个字符串"3581169b064f71be1630b321d3ca318f"会被转换为整数3581169。所以这就是为什么你的第二个测试不起作用的原因。
你应该做的是:
你可能想要:
if ($test1 === "0")

请注意使用三个等号而不是两个等号。这样可以确保你比较的是一个只包含数字零的字符串,并防止任何类型转换。

8
经过一些调查,发现 PHP 手册中的 aidan 提到,任何不以数字开头的字符串在转换为整数时都会被转换为 0。
这意味着:
("php" == 0) === true
("1php" == 0) === false

非常令人恼火且文档不完善。它出现在类型比较页面的评论底部。

1
是的,我想就是这样。文档确实很差。 - Paolo Bergantino
1
我不认为这是糟糕的文档。隐式类型转换是 PHP 的基础之一,在手册的类型部分,特别是类型转换部分中有很好地解释。== 是一种松散类型比较;使用 === 可以帮助防止混淆情况的发生。 - thomasrutter
请参阅 http://us3.php.net/manual/en/language.types.string.php#language.types.string.conversion 中的“字符串转换为数字”部分。 - thomasrutter

2
$test1 = "d85d1d81b25614a3504a3d5601a9cb2e";

这个字符串以“d”开头,不是有效的数字,变量将解析为0,你的测试#1将通过。

$test2 = "3581169b064f71be1630b321d3ca318f";

这个字符串以3581169开头,它是一个有效的数字,所以变量将被解析为该值,而不是0。因此你的第二个测试用例将不会通过。


1

我一直在使用一些转换和比较来测试一个数字字符串是否为数字:

$test1="19de6a91d2ca9d91721d82f1bd8102b6";
   echo (float)$test1==$test1; //TRUE
   echo is_float($test1); //FALSE

   //Converting the string to float and then converting it to string and compare will do the trick 
   echo (string)((float)$test1)==(string)$test1; //FALSE


$test2="5.66";
   echo (float)$test2==$test2; //TRUE

   //Testing the numeric string using `is_float` wont give the expected result
   echo is_float($test2); //FALSE

   echo (string)((float)$test2)==(string)$test2; //TRUE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接