PHP 內建 substr() 按位元組截取,對多位元組編碼(UTF-8 中文佔 3 位元組)會產生「半個字元」導致亂碼。本文系統梳理三種解決方案:mbstring 擴展函數、自定義位元組檢測函數、輸出端截斷技巧,並對比 mb_substr 與 mb_strcut 的本質差異。
確認 php.ini 已啟用 extension=mbstring,並設定 mbstring.internal_encoding = UTF-8。
| 函數 | 單位 | 行為 | 適用場景 |
|---|---|---|---|
mb_substr($str, $start, $len, 'UTF-8') | 字元 | 按 Unicode 字元截取,不會產生半字元 | 顯示摘要、標題截斷、長度統計 |
mb_strcut($str, $start, $len, 'UTF-8') | 位元組 | 按位元組截取,但會自動避開半字元(截到完整字元為止) | 需嚴格控制位元組長度(如資料庫 VARCHAR 長度) |
$str = '這樣一來我的字符串就不會有亂碼^_^'; echo mb_substr($str, 0, 7, 'UTF-8'); // 輸出:這樣一來我的字(7 個字元) echo mb_strcut($str, 0, 7, 'UTF-8'); // 輸出:這樣一(約 7 位元組,截在完整字元邊界)
原理:遍歷字串,檢測 ord($char) > 127 判斷是否為多位元組字元首位元組(UTF-8 首位元組最高位為 1),若是則一次取 2~4 位元組(視編碼而定,簡體中文常見 3 位元組,此處簡化為 2 位元組示意)。
/**
* 純 PHP 實現中英混排字串截取(UTF-8)
* @param string $str 來源字串
* @param int $len 目標字元數(中英各算 1)
* @param string $suffix 截斷後綴
* @return string
*/
function substr_utf8_mixed($str, $len, $suffix = '...') {
$result = '';
$count = 0;
$bytes = strlen($str);
for ($i = 0; $i < $bytes && $count < $len; $i++) {
$char = $str[$i];
$ord = ord($char);
if ($ord < 128) { // ASCII
$result .= $char;
$count++;
} elseif (($ord & 0xE0) === 0xC0) { // 2-byte sequence
$result .= $str[$i] . $str[$i+1];
$i++; $count++;
} elseif (($ord & 0xF0) === 0xE0) { // 3-byte sequence (中文常見)
$result .= $str[$i] . $str[$i+1] . $str[$i+2];
$i += 2; $count++;
} elseif (($ord & 0xF8) === 0xF0) { // 4-byte sequence (Emoji 等)
$result .= $str[$i] . $str[$i+1] . $str[$i+2] . $str[$i+3];
$i += 3; $count++;
}
}
return ($count < mb_strlen($str, 'UTF-8')) ? $result . $suffix : $result;
}
// 測試
$str = "34中華人民共和國56";
echo substr_utf8_mixed($str, 3); // 輸出:34中...
優點:無擴展依賴。缺點:效能較低、需自行處理邊界與錯誤編碼。
// 利用 chr(0) 截斷輸出緩衝,瀏覽器不會顯示後續內容 echo substr($str, 0, 30) . chr(0);
此法不改變字串本身,僅影響輸出,不建議用於資料處理或存儲。
mb_strlen、mb_strpos、mb_strtolower 等)。VARCHAR(255) CHARACTER SET utf8mb4,按字元長度限制,避免應用層截取。text-overflow: ellipsis 處理單行截斷,複雜場景用 JS 處理。mb_substr($str, 0, 10) 若省略 encoding 參數,預設使用 mbstring.internal_encoding,若未設定則回退到 ISO-8859-1 導致錯誤 → 建議顯式傳遞 'UTF-8'。mb_substr 正確視為 1 字元,自定義函數需支援 4-byte 序列。strlen() 回傳位元組數,mb_strlen() 回傳字元數,勿混淆。中文字串截取的核心是字元邊界識別。現代 PHP 專案請務必啟用 mbstring 並統一使用 mb_substr() / mb_strlen() 等函數族;舊環境或嵌入式場景可採用自定義函數並加強測試。切記:資料層面按字元存儲,展示層面按視覺截斷,分層處理最穩健。