在 PHP 開發中,strlen() 與 mb_strlen() 是計算字符串長度的兩大核心函數。雖然在純 ASCII 字符串下表現一致,但一旦涉及中英文混排、多編碼環境,兩者機制差異將直接導致截取亂碼、對齊錯位、存儲溢出等隱性 Bug。本文從底層原理、多編碼實測、佔位符計算模型、擴展配置四個維度,構建完整知識體系。
| 維度 | strlen() | mb_strlen() |
|---|---|---|
| 計數本質 | 字節數 | 字符數 |
| 編碼感知 | 無(二進制安全) | 有(需指定 charset) |
| 中文處理(UTF-8) | 1 字符 = 3 字節 | 1 字符 = 1 長度 |
| 中文處理(GBK/GB2312) | 1 字符 = 2 字節 | 1 字符 = 1 長度 |
| 擴展依賴 | 核心內置 | 需 mbstring 擴展 |
| 性能 | O(1) 極快 | O(n) 遍歷解碼 |
測試字符串:$str = '中文a字1符'(4 中文 + 2 ASCII)
<?php
// 文件必須保存為 UTF-8 編碼
$str = '中文a字1符';
var_dump([
'strlen' => strlen($str), // 14
'mb_strlen_UTF8' => mb_strlen($str, 'UTF-8'), // 6
'mb_strlen_GBK' => mb_strlen($str, 'GBK'), // 8
'mb_strlen_GB2312' => mb_strlen($str, 'GB2312'), // 10
'internal_enc' => mb_internal_encoding(), // 當前內部編碼
]);
?>
鐵律:mb_strlen 第二參數 必須與字符串實際編碼一致。生產環境統一 UTF-8 並顯式傳參 'UTF-8'。
在等寬字體環境(終端、表格、PDF、代碼編輯器)中,中文字符通常佔 2 列寬度,ASCII 佔 1 列。利用兩函數差值可無需正則、無需遍歷字符即可快速計算:
<?php
function getVisualWidth(string $str, string $charset = 'UTF-8'): int
{
$byteLen = strlen($str);
$charLen = mb_strlen($str, $charset);
// 推導:UTF-8 中文 3字節/字符,ASCII 1字節/字符
// 視覺寬度 = (字節數 + 字符數) / 2
// 證明:中文貢獻 (3+1)/2=2,ASCII 貢獻 (1+1)/2=1
return (int)(($byteLen + $charLen) / 2);
}
// 驗證
$tests = [
'中文a字1符' => 10, // 4中文×2 + 2ASCII×1 = 10
'Hello世界' => 7, // 5ASCII×1 + 2中文×2 = 7
'12345' => 5, // 純數字
'測試' => 4, // 純中文
];
foreach ($tests as $str => $expected) {
$actual = getVisualWidth($str);
echo "$str => $actual (預期 $expected) " . ($actual === $expected ? '✓' : '✗') . PHP_EOL;
}
?>
mb_substr 配合視覺寬度)VARCHAR(200) 實際可存中文約 66 個)php -m | grep mbstring 或 extension_loaded('mbstring')。extension=php_mbstring.dllextension=mbstring; 註釋mb_internal_encoding('UTF-8'),避免漏傳參時使用不可控的默認編碼。mbstring.encoding_translation = On 自動轉碼,現代框架建議關閉,自行控制編碼邊界。mbstring.func_overload 若設置為非 0,會覆蓋 strlen、substr 等核心函數,導致第三方庫行為異常。生產環境強制設置為 0。count() 僅用於 統計數組元素個數 或 實現 Countable 接口的對象屬性數。對字符串調用:PHP 7.2+ 拋出警告並返回 1(視為單元素數組),語義完全無關。
strlen($str) | 性能最優 |
| 多語言字符數 | mb_strlen($str, 'UTF-8') | 必須顯式指定編碼 |
| 視覺寬度/佔位 | (strlen + mb_strlen) / 2 | 利用字節/字符差值反推 |
| 截取前 N 字符 | mb_substr($str, 0, $n, 'UTF-8') | 避免截半個中文產生亂碼 |
| 判斷空字符串 | $str === '' | 比長度比較更快、語義更清晰 |
| 高頻循環中計算長度 | 緩存結果或用 strlen 估算 | mb_strlen 遍歷開銷大 |
建立「字節≠字符、編碼必須顯式、視覺寬度可推導」的思維模型,能讓你在國際化文本處理、報表生成、數據驗證等高頻場景游刃有餘。下次遇到「明明只有 10 個字卻報 VARCHAR(20) 溢出」「截取前 5 個字符卻只顯示 2 個中文」等問題,請優先檢查:是否混淆了字節與字符語義?mb_strlen 是否顯式指定了 UTF-8?