字串截取是高頻操作,但 `substr` 遇到中文會產生亂碼,`mb_substr` 與 `mb_strcut` 雖支援多位元組,卻因「字元」與「位元組」單位不同而導致結果差異。本文深度比較三函式機制、實測案例、選擇指南與封裝建議。
| 函式 | 簽名 | 單位 | 多位元組安全 | 半字元處理 | 適用場景 |
|---|---|---|---|---|---|
substr | substr(string, start, length) | 位元組 | ❌ | 直接切半 → 亂碼 | 純 ASCII、二進位資料 |
mb_substr | mb_substr(string, start, length, encoding) | **字元** | ✅ | 不切半字元 | **一般文字截取、摘要、標題** |
mb_strcut | mb_strcut(string, start, length, encoding) | **位元組** | ✅ | 從字元邊界開始切,不產生半字元 | 固定位元組長度輸出(如 DB CHAR 欄位) |
$s = '這樣一來我的字符串就不會有亂碼^_^';
echo "mb_substr(0,7): " . mb_substr($s, 0, 7, 'utf-8') . "\n"; // 這樣一來我的字 (7 字元)
echo "mb_strcut(0,7): " . mb_strcut($s, 0, 7, 'utf-8') . "\n"; // 這樣一 (7 位元組 ≈ 2.3 字元)
$s2 = '飛花院博客feihuayuan';
echo "mb_substr(0,9): " . mb_substr($s2, 0, 9, 'utf-8') . "\n"; // 飛花院博客feih (9 字元)
echo "mb_strcut(0,9): " . mb_strcut($s2, 0, 9, 'utf-8') . "\n"; // 飛花院 (9 位元組 = 3 中文字)
$s3 = '我是一串比較長的中文-';
echo "mb_substr(0,6): " . mb_substr($s3, 0, 6, 'utf-8') . "\n"; // 我是一串比較
echo "mb_strcut(0,6): " . mb_strcut($s3, 0, 6, 'utf-8') . "\n"; // 我是
$s = '中文abc'; // UTF-8: 中(3) 文(3) a(1) b(1) c(1) = 9 bytes
mb_strcut($s, 0, 1); // '' (第 1 byte 非字元邊界,退到字元開頭 → 0 長度)
mb_strcut($s, 0, 2); // '' (同理)
mb_strcut($s, 0, 3); // '中' (剛好 1 個中文字元)
mb_strcut($s, 0, 4); // '中' (第 4 byte 屬於「文」字元,退到「文」開頭 → 仍只取「中」)
mb_strcut($s, 0, 6); // '中文' (2 個中文字元 = 6 bytes)
**規則**:`mb_strcut` 會將 `start` 向前調整到最近的字元邊界,再截取 `length` 位元組,最後再向後調整結束位置到字元邊界。**保證不產生半個字元**。
/**
* 多位元組安全截斷(按字元數)
*/
function mb_truncate($string, $length, $etc = '...', $encoding = 'UTF-8') {
if (mb_strlen($string, $encoding) <= $length) return $string;
$etcLen = mb_strlen($etc, $encoding);
$cutLen = max(0, $length - $etcLen);
return mb_substr($string, 0, $cutLen, $encoding) . $etc;
}
/**
* 固定位元組截斷(不產生半字元)
*/
function mb_cut_bytes($string, $bytes, $encoding = 'UTF-8') {
return mb_strcut($string, 0, $bytes, $encoding);
}
// 使用
echo mb_truncate('這是一個很長的標題需要截斷', 10); // 這是一個很長的...
echo mb_cut_bytes('中文abc', 6); // 中文
**`mb_substr` 按字元截取,`mb_strcut` 按位元組截取**,兩者皆保證不產生半個字元。**90% 場景用 `mb_substr`**;只有「必須輸出固定位元組長度」才用 `mb_strcut`。專案統一 UTF-8、設定 `mb_internal_encoding('UTF-8')`、封裝工具函式,即可徹底解決中文截取亂碼問題。