PHP 提供 strlen() 與 mb_strlen() 兩個計算字符串長度的函數。在純英文環境下二者無差異,但一旦引入中文等多字節字符,字節計數與字符計數的本質區別將導致截取、驗證、顯示等環節出現難以排查的 Bug。本文通過原理剖析、多編碼實測、視覺寬度推導模型、擴展配置指南,建立完整認知框架。
<?php
// 文件編碼:UTF-8
$str = '中文a字1符'; // 4中文 + 2ASCII
echo "strlen: " . strlen($str) . PHP_EOL; // 14 字節
echo "mb_strlen UTF-8: " . mb_strlen($str, 'UTF-8') . PHP_EOL; // 6 字符
echo "mb_strlen GBK: " . mb_strlen($str, 'GBK') . PHP_EOL; // 8(編碼不匹配產生亂碼計數)
echo "mb_strlen GB2312: " . mb_strlen($str, 'GB2312') . PHP_EOL; // 10
?>
| 函數 | 計數單位 | 中文處理 (UTF-8) | 編碼參數 | 性能 |
|---|---|---|---|---|
| strlen | 字節 | 3 字節/字 | 無 | O(1) 極快 |
| mb_strlen | 字符 | 1 長度/字 | 必需(建議顯式 'UTF-8') | O(n) 遍歷解碼 |
等寬字體下:中文佔 2 列,ASCII 佔 1 列。利用 strlen(字節數)與 mb_strlen(字符數)的線性關係:
<?php
function visualWidth($str, $charset = 'UTF-8') {
return (int)((strlen($str) + mb_strlen($str, $charset)) / 2);
}
// 驗證
$cases = ['中文a字1符'=>10, 'Hello世界'=>7, '123'=>3, '測試'=>4];
foreach ($cases as $s => $exp) {
echo "$s => " . visualWidth($s) . " (預期 $exp)" . PHP_EOL;
}
?>
數學證明:UTF-8 中文 3 字節 + 1 字符 = 4 → /2 = 2 寬度;ASCII 1 字節 + 1 字符 = 2 → /2 = 1 寬度。完美匹配視覺模型。
extension=mbstring (php.ini 取消註釋)mb_internal_encoding('UTF-8') 統一內部編碼mbstring.func_overload = 0(防止覆蓋 strlen/substr 等核心函數)php -m | grep mbstring 確認加載mb_strlen($str) 不傳編碼 → 依賴不可控的內部編碼strlen 判斷中文字符數 → 實際獲取字節數count($str) 計算長度 → PHP 7.2+ 報警告,返回 1mb_substr($str, 0, $n, 'UTF-8')VARCHAR(200) 實際可存 ~66 中文(200/3)count() 專用於數組元素計數或Countable 對象,與字符串長度無任何關係。
掌握「字節≠字符、編碼必須顯式、視覺寬度可推導」三大核心認知,配合 mbstring 正確配置,即可徹底解決 PHP 多語言環境下的字符串長度計算難題。