PHP 內置 strlen() 返回的是字符串的字節數,而非字符數。對於 UTF-8 編碼,一個漢字佔 3 字節,英文字母佔 1 字節,直接使用 strlen() 會導致長度計算錯誤,影響表單驗證、截取、對齊等邏輯。
<?php
$str1 = 'Hello world!中';
echo strlen($str1); // 輸出 14 (12字母+1空格+1感嘆號+3字節中文) - 實際字符數 13
$str2 = '你好,世界!';
echo strlen($str2); // UTF-8 下輸出 18 (6漢字×3) - 實際字符數 6
?>
PHP mbstring 擴展提供多字節字符串函數,mb_strlen() 可正確返回字符數。
<?php
$str = '你好,世界!';
echo mb_strlen($str, 'UTF-8'); // 輸出 6
echo mb_strlen($str); // 使用內部編碼(需 php.ini 設置 mbstring.internal_encoding)
?>
mbstring 擴展(現代 PHP 環境默認開啟)。'UTF-8' 最安全,避免依賴全局配置。<?php
function utf8_strlen($string = null) {
if ($string === null) return 0;
// /u 修飾符按 Unicode 字符匹配,/s 讓 . 匹配換行
preg_match_all('/./us', $string, $matches);
return count($matches[0]);
}
echo utf8_strlen('你好,世界!'); // 6
echo utf8_strlen('Hello 世界'); // 8
?>
原理: preg_match_all('/./us', ...) 將字符串分解為單個 Unicode 碼點,統計匹配數即為字符數。
<?php
function mb_strlen_compat($str) {
// 嘗試將 GBK 轉為 UTF-8(若已是 UTF-8 則 iconv 返回 false 或原串)
$utf8 = @iconv('GBK', 'UTF-8//IGNORE', $str);
if ($utf8 !== false && $utf8 !== '') {
$str = $utf8;
}
// 統計 Unicode 字符
preg_match_all('/./us', $str, $matches);
return count($matches[0]);
}
// 測試
$gbk_str = iconv('UTF-8', 'GBK', '測試字符串');
echo mb_strlen_compat($gbk_str); // 正確輸出 5
echo mb_strlen_compat('測試字符串'); // 正確輸出 5
?>
| 函數 | 返回值含義 | UTF-8 中文 | 適用場景 |
|---|---|---|---|
strlen() | 字節數 | 1 個漢字 = 3 | 二進制數據、文件大小、原始字節操作 |
mb_strlen() | 字符數 | 1 個漢字 = 1 | 標準首選,表單驗證、截取、顯示長度 |
preg_match_all('/./us') | 字符數 | 1 個漢字 = 1 | 無 mbstring 環境、兼容性代碼 |
iconv_strlen() | 字符數 | 1 個漢字 = 1 | 需 iconv 擴展,功能同 mb_strlen |
mb_strlen(),在框架基類或工具類中封裝 str_len($str) 統一入口。mbstring.internal_encoding = UTF-8,mbstring.func_overload = 0(不覆蓋原生函數)。mb_substr()、mb_strcut() 使用,切勿用 substr()。VARCHAR(255) 指字符數(utf8mb4),不受字節影響,但 PHP 端驗證必須用字符數。str.length 返回 UTF-16 代碼單元數(Emoji 算 2),PHP 端用 mb_strlen(),雙端驗證邏輯需對齊。<?php
/**
* 獲取字符串字符長度(兼容 UTF-8/GBK,優先使用 mbstring)
* @param string $str
* @return int
*/
function str_len($str) {
if (!is_string($str)) $str = (string)$str;
if (function_exists('mb_strlen')) {
return mb_strlen($str, 'UTF-8');
}
// 降級方案
$utf8 = @iconv('GBK', 'UTF-8//IGNORE', $str);
if ($utf8 !== false && $utf8 !== '') $str = $utf8;
preg_match_all('/./us', $str, $m);
return count($m[0]);
}
/**
* 安全截取字符串
* @param string $str
* @param int $start
* @param int|null $length
* @return string
*/
function str_sub($str, $start, $length = null) {
if (function_exists('mb_substr')) {
return mb_substr($str, $start, $length, 'UTF-8');
}
// 降級:正則分解後切片
preg_match_all('/./us', $str, $m);
$chars = $m[0];
return implode('', array_slice($chars, $start, $length));
}
?>
正確處理多字節字符串長度是 PHP 國際化開發的基礎功課。優先使用 mbstring 函數族,無擴展環境下用正則降級,封裝統一工具函數,從源頭杜絕長度計算錯誤導致的業務 Bug。