mb_substr($str, $start, $length, $encoding) 的 $length 參數是「字符數」,而非「字節數」。無論編碼為 UTF-8 (中文 3 字節) 或 GBK (中文 2 字節),mb_substr($str, 0, 2, 'UTF-8') 都返回前 2 個字符。
$str = "中華人民共和國"; // 7 個中文字符
// UTF-8 環境
echo "UTF-8:\n";
echo mb_substr($str, 0, 2, 'UTF-8') . "\n"; // 中華 (2字符)
echo mb_substr($str, 0, 3, 'UTF-8') . "\n"; // 中華人 (3字符)
echo "字節長度: " . strlen($str) . "\n"; // 21 字節 (7*3)
// GBK 環境 (需文件保存為 GBK 或 iconv 轉換)
$gbkStr = iconv('UTF-8', 'GBK', $str);
echo "\nGBK:\n";
echo mb_substr($gbkStr, 0, 2, 'GBK') . "\n"; // 中華 (2字符)
echo "字節長度: " . strlen($gbkStr) . "\n"; // 14 字節 (7*2)
// 不指定編碼 (使用 mbstring.internal_encoding)
mb_internal_encoding('UTF-8');
echo "\n無編碼參數 (internal=UTF-8):\n";
echo mb_substr($str, 0, 2) . "\n"; // 中華
| 誤區 | 正確理解 |
|---|---|
| 以為 $length 是字節數 | 是字符數 (Code Points) |
| 以為 UTF-8 中文佔 3 字節所以 length=6 才能取 2 個字 | length=2 即可取 2 個字 |
| 混用 substr (字節截取) 導致中文截半 | 多字節編碼必須用 mb_substr |
| 以為 mb_substr 會自動補全截斷的字符 | 不會,嚴格按字符邊界截取 |
$str = "abc中文";
// substr (字節截取)
echo substr($str, 0, 5) . "\n"; // abc中 (第 5 字節截斷「中」的第一字節,顯示亂碼)
// mb_substr (字符截取)
echo mb_substr($str, 0, 5, 'UTF-8') . "\n"; // abc中文 (前 5 字符)
function mbTruncate($str, $maxLen, $suffix = '...', $encoding = 'UTF-8') {
$strLen = mb_strlen($str, $encoding);
if ($strLen <= $maxLen) return $str;
return mb_substr($str, 0, $maxLen - mb_strlen($suffix, $encoding), $encoding) . $suffix;
}
echo mbTruncate("這是一個很長的標題需要截取", 10); // 這是一個很長的...
echo mbTruncate("Short", 10); // Short
mb_substr 需掃描字節確定字符邊界,極長字符串 (MB 級) 反複調用較慢。mb_strlen 判斷是否需要截取。mb_substr 已優化,一般場景無性能瓶頸。記住:**mb_* 系列函數參數均以「字符」為單位**。只需設置正確的編碼 (或 mb_internal_encoding),即可正確處理多語言文本,無需手動計算字節數。