2024 日本 PHP 研討會

strcmp

(PHP 4, PHP 5, PHP 7, PHP 8)

strcmp二進位制安全字串比較

說明

strcmp(字串 $string1, 字串 $string2): 整數

請注意,此比較區分大小寫。

參數

string1

第一個字串。

string2

第二個字串。

返回值

如果 string1 小於 string2,則返回 -1;如果 string1 大於 string2,則返回 1;如果兩者相等,則返回 0

更新日誌

版本 說明
8.2.0 此函數現在返回 -11,而之前返回的是負數或正數。

範例

範例 #1 strcmp() 範例

<?php
$var1
= "Hello";
$var2 = "hello";
if (
strcmp($var1, $var2) !== 0) {
echo
'$var1 與 $var2 在區分大小寫的字串比較中不相等';
}
?>

參見

  • strcasecmp() - 二進制安全且不區分大小寫的字串比較
  • preg_match() - 執行正規表達式比對
  • substr_compare() - 從偏移量開始,最多比較 length 個字元的二進制安全字串比較
  • strncmp() - 前 n 個字元的二進制安全字串比較
  • strstr() - 尋找字串首次出現的位置
  • substr() - 返回字串的一部分

新增註解

使用者貢獻的註解 16 則註解

jendoj at gmail dot com
12 年前
如果您依賴 strcmp 進行安全的字串比較,兩個參數都必須是字串,否則結果將非常難以預測。
例如,您可能會得到意外的 0,或 NULL、-2、2、3 和 -3 等返回值。

strcmp("5", 5) => 0
strcmp("15", 0xf) => 0
strcmp(61529519452809720693702583126814, 61529519452809720000000000000000) => 0
strcmp(NULL, false) => 0
strcmp(NULL, "") => 0
strcmp(NULL, 0) => -1
strcmp(false, -1) => -2
strcmp("15", NULL) => 2
strcmp(NULL, "foo") => -3
strcmp("foo", NULL) => 3
strcmp("foo", false) => 3
strcmp("foo", 0) => 1
strcmp("foo", 5) => 1
strcmp("foo", array()) => NULL + PHP 警告
strcmp("foo", new stdClass) => NULL + PHP 警告
strcmp(function(){}, "") => NULL + PHP 警告
lehal2 at hotmail dot com
11 年前
我希望這能讓您清楚地了解 strcmp 的內部運作方式。

<?php
$str1
= "b";
echo
ord($str1); //98
echo "<br/>";
$str2 = "t";
echo
ord($str2); //116
echo "<br/>";
echo
ord($str1)-ord($str2);//-18
$str1 = "bear";
$str2 = "tear";
$str3 = "";
echo
"<pre>";
echo
strcmp($str1, $str2); // -18
echo "<br/>";
echo
strcmp($str2, $str1); //18
echo "<br/>";
echo
strcmp($str2, $str2); //0
echo "<br/>";
echo
strcmp($str2, $str3); //4
echo "<br/>";
echo
strcmp($str3, $str2); //-4
echo "<br/>";
echo
strcmp($str3, $str3); // 0
echo "</pre>";
?>
Rob Wiesler
15 年前
一個重要的注意事項 - 從反引號操作擷取的字串可能以零結尾(C 語言風格),因此將不等於 PHP 中常見的非零結尾字串(大致上是 Pascal 語言風格)。解決方法是用 trim() 函式包圍每個 `` 對或 shell_exec() 函式。這也可能是其他呼叫 shell 的函式會遇到的問題;我沒仔細檢查過。

在 Debian Lenny(以及 RHEL 5,略有不同)上,我會得到以下結果

====PHP====
<?php
$sz
= `pwd`;
$ps = "/var/www";

echo
"以零結尾的字串:<br />sz = ".$sz."<br />str_split(sz) = "; print_r(str_split($sz));
echo
"<br /><br />";

echo
"Pascal 風格字串:<br />ps = ".$ps."<br />str_split(ps) = "; print_r(str_split($ps));
echo
"<br /><br />";

echo
"一般的比較結果:<br />";
echo
"sz == ps = ".($sz == $ps ? "true" : "false")."<br />";
echo
"strcmp(sz,ps) = ".strcmp($sz,$ps);
echo
"<br /><br />";

echo
"使用 trim() 處理後的零結尾字串之比較結果:<br />";
echo
"trim(sz) = ".trim($sz)."<br />";
echo
"str_split(trim(sz)) = "; print_r(str_split(trim($sz))); echo "<br />";
echo
"trim(sz) == ps = ".(trim($sz) == $ps ? "true" : "false")."<br />";
echo
"strcmp(trim(sz),ps) = ".strcmp(trim($sz),$ps);
?>

====輸出====
以零結尾的字串:
sz = /var/www
str_split(sz) = 陣列 ( [0] => / [1] => v [2] => a [3] => r [4] => / [5] => w [6] => w [7] => w [8] => )

Pascal 風格字串:
ps = /var/www
str_split(ps) = 陣列 ( [0] => / [1] => v [2] => a [3] => r [4] => / [5] => w [6] => w [7] => w )

一般的比較結果:
sz == ps = false
strcmp(sz,ps) = 1

使用 trim() 處理後的零結尾字串之比較結果:
trim(sz) = /var/www
str_split(trim(sz)) = 陣列 ( [0] => / [1] => v [2] => a [3] => r [4] => / [5] => w [6] => w [7] => w )
trim(sz) == ps = true
strcmp(trim(sz),ps) = 0
kgun ! mail ! com
5 年前
如果您希望結果總是 -1、0 或 1,就像 JS 的 indexOf() 一樣;

<?php
function cmp(string $str1, string $str2): int {
return (
$str1 > $str2) - ($str1 < $str2);
}

$str1 = 'a';
$str2 = 'z';
var_dump(cmp($str1, $str2), strcmp($str1, $str2));

//=> int(-1) int(-25) int(-25)

$str1 = 'a';
$str2 = '1';
var_dump(cmp($str1, $str2), strcmp($str1, $str2));
//=> int(1) int(48) int(48)
?>
frewuill at merlin-corp dot com
24 年前
請確定您要比較的字串沒有特殊字元,例如 '\n' 或類似的字元。
luizvid at gmail dot com
9 年前
如果兩個字串不相同,strcmp 會回傳 -1 或 1,
如果兩個字串相同則回傳 0,除非比較一個字串和一個空字串 (<?php $a = ""; ?>),它會回傳字串的長度。

例如:
<?php
$a
= "foo"; // 長度 3
$b = ""; // 空字串
$c = "barbar"; // 長度 6

echo strcmp($a, $a); // 輸出 0
echo strcmp($a, $c); // 輸出 1
echo strcmp($c, $a); // 輸出 -1
echo strcmp($a, $b); // 輸出 3
echo strcmp($b, $a); // 輸出 -3
echo strcmp($c, $b); // 輸出 6
echo strcmp($b, $c); // 輸出 -6
?>
erik at eldata dot se
3 年前
strcmp 和 strcasecmp 無法有效處理多位元組 (UTF8) 字串,而且也沒有 mb_strcmp 或 mb_strcasecmp 函式。建議使用功能更強大的 Collator 類別及其 compare 方法(請搜尋上方的 Collator)—— 它不僅支援 UTF8,還支援不同的國家/地區排序規則(排序順序)。

也支援自然排序,使用 setAttribute 方法將 Collator::NUMERIC_COLLATION 設定為 Collator::ON 即可。
hrodicus at gmail dot com
13 年前
請注意 5.2 和 5.3 版本之間的差異

echo (int)strcmp('pending',array());
在 PHP 5.2.16(可能在所有 5.3 之前的版本)中會輸出 -1
但在 PHP 5.3.3 中會輸出 0

當然,您永遠不需要在字串比較中使用陣列作為參數。
jcanals at totsoft dot com
20 年前
關於西班牙語地區設定的一些注意事項。我看過一些說明指出「CH」、「RR」或「LL」在西班牙語中必須被視為單個字母。這並非完全正確。「CH」、「RR」和「LL」在過去(很多年前)曾被視為單個字母,因此您必須使用「傳統排序」。如今,西班牙皇家學院使用現代排序,並建議不再將「CH」、「RR」和「LL」視為單個字母。它們必須被視為兩個獨立的字母,並以此方式進行排序和比較。

您只需查看西班牙皇家學院官方辭典,就可以看到很多年前就沒有針對「CH」、「LL」或「RR」的獨立章節了……例如,以 CH 開頭的單字必須排在以 CG 開頭的單字之後,以及以 CI 開頭的單字之前。
chris at unix-ninja dot com
11 年前
由於有些人可能不清楚,請注意此函式還有另一個可能的返回值。

strcmp() 失敗時會返回 NULL。

這會導致在使用等於比較 (==) 時等同於匹配。
建議您使用完全相同比較 (===) 來測試匹配,這樣就不會將 NULL 返回值視為匹配。

---------------------
範例
---------------------

$variable1 = array();
$ans === strcmp($variable1, $variable2);

這將阻止 $ans 返回匹配結果;

比較使用者輸入時請謹慎使用 strcmp(),因為這可能會對您的程式碼產生潛在的安全風險。
mikael1 at mail dot ru
5 年前
1) 如果兩個字串的開頭部分相同,則會從兩個字串中截斷該部分。
2) 將剩餘的字串進行比較,有兩種可能的結果
a) 如果其中一個剩餘的字串是空字串,則返回非空字串的長度(正負號取決於您將參數傳遞給函式的順序)
b) 在其他任何情況下,只比較第一個字元的數值。結果為 +1 或 -1,無論兩個數值之間的差異有多大。

<?php
$str
= array('','a','afox','foxa');
$size = count($str);

echo
'<pre>';
for(
$i=0; $i<$size; $i++)
{
for(
$j=$i+1; $j<$size; $j++)
{
echo
'<br>('.$str[$i].','.$str[$j].') = '.strcmp($str[$i], $str[$j]);
echo
'<br>('.$str[$j].','.$str[$i] .') = '.strcmp($str[$j], $str[$i]);
}
}
echo
'</pre>';
?>

在 Apache/2.4.37 (Win32) OpenSSL/1.1.1 PHP/7.2.12 產生以下結果

(,a) = -1 //與空字串比較會產生非空字串的長度
(a,) = 1 // 同上
(,afox) = -4 // 同上
(afox,) = 4 // 同上
(,foxa) = -4 // 同上
(foxa,) = 4 // 同上
(a,afox) = -3 // 相同的開頭部分 ("a") 會從兩個字串中截斷。然後剩餘的 "fox" 會與另一個參數中剩餘的空字串進行比較。產生非空字串的長度。與上述所有範例相同。
(afox,a) = 3 // 同上
(a,foxa) = -1 // 沒有要截斷的部分。僅比較第一個字母的數值
(foxa,a) = 1 // 同上
(afox,foxa) = -1 // 同上
(foxa,afox) = 1 // 同上
匿名
22 年前
總之,strcmp() 不一定像在 'C' 語系環境中那樣使用每個字元的 ASCII 碼順序,而是會解析每個字串以匹配特定語言的字元實體(例如西班牙語中的 'ch' 或捷克語中的 'dz'),然後比較它們的排序順序。當兩個字元實體具有相同的排序順序時(例如德語中的 'ss' 和 'ß'),strcmp() 會根據它們的程式碼比較它們,或者 strcasecmp() 會將它們視為相等。
接著會考慮 LC_COLLATE 語系環境設定:只有在 LC_COLLATE=C 或 LC_ALL=C 時,strcmp() 才會按字元程式碼比較字串。
一般來說,大多數語系環境定義了以下順序
控制字元、空格、標點符號和底線、數字、字母(拉丁文字中,小寫字母在先,大寫字母在後;阿拉伯文字中,詞尾形式、詞中形式、獨立形式,然後是詞首形式)、符號、其他...
使用 strcasecmp() 時,會忽略字母子類別,並將所有形式的字母視為相等。
另請注意,某些地區設定在處理帶重音符號的字元時會有不同的行為:有些地區設定認為它們與未帶重音符號的字母相同(排序順序略有不同,例如法語、義大利語、西班牙語),有些地區設定則認為它們是不同的字母,具有獨立的排序順序(例如 C 地區設定,或北歐語言)。
最後,排序字串並非考慮個別字元,而是考慮組成單個字母的字元群組
- 例如西班牙語中的「ch」或「CH」,它總是在所有以「c」或「C」開頭的其他字串之後,包括「cz」,但在「d」或「D」之前;
- 德語中的「ss」和「ß」;
- 使用拉丁字母書寫的某些中歐語言中的「dz」、「DZ」和「Dz」…
- 地區設定的 UTF-8、UTF-16 (Unicode)、S-JIS、Big5、ISO2022 字元編碼(地區設定名稱中的後綴)會先將字元解碼為 UCS4/ISO10646 字碼位置,然後再應用主要地區設定指定的語言規則…
因此,在考慮「字元」時要格外小心,因為它可能僅表示一個編碼位元組,在字串排序演算法中沒有任何意義:西班牙語中字串「cholera」的第一個字元是「ch」,而不是「c」!
wsogmm at seznam dot cz
15 年前
針對 arnar at hm dot is 的註解補充說明:md5() 是一個雜湊函式,因此可能會發生(儘管機率很小)兩個不同字串的 md5() 校驗和相同的情況(雜湊碰撞)…
kamil dot k dot kielczewski at gmail dot com
7 年前
漏洞(在 PHP >=5.3 中)

<?php
if (strcmp($_POST['password'], 'sekret') == 0) {
echo
"歡迎,已授權的使用者!\n";
} else {
echo
"走開,冒名頂替者。\n";
}
?>

$ curl -d password=sekret http://andersk.scripts.mit.edu/strcmp.php
歡迎,已授權的使用者!

$ curl -d password=wrong http://andersk.scripts.mit.edu/strcmp.php
走開,冒名頂替者。

$ curl -d password[]=wrong http://andersk.scripts.mit.edu/strcmp.php
歡迎,已授權的使用者!

此範例的程式碼來源:https://www.quora.com/Why-is-PHP-hated-by-so-many-developers
francis at flourish dot org
18 年前
如果您想根據地區設定比較字串,請使用 strcoll。
To Top