在 PHP 開發中,處理 UTF-8 編碼的中英文混合字符串截取是常見需求。由於中文字符在 UTF-8 中佔 3 個位元組,英文字符佔 1 個位元組,直接使用 substr 會導致亂碼。本文將介紹幾種實用的截取方案,並分析其優缺點。
這是一種經典的不依賴擴展的實現方式,通過檢測字符的 ASCII 值來判斷字符類型:
function cut_str($sourcestr, $cutlength, $addstr = '...') {
$returnstr = '';
$i = 0;
$n = 0;
$str_length = strlen($sourcestr);
while (($n < $cutlength) && ($i <= $str_length)) {
$temp_str = substr($sourcestr, $i, 1);
$ascnum = ord($temp_str);
if ($ascnum >= 224) {
$returnstr .= substr($sourcestr, $i, 3);
$i += 3;
$n++;
} elseif ($ascnum >= 192) {
$returnstr .= substr($sourcestr, $i, 2);
$i += 2;
$n++;
} elseif ($ascnum >= 65 && $ascnum <= 90) {
$returnstr .= substr($sourcestr, $i, 1);
$i += 1;
$n++;
} else {
$returnstr .= substr($sourcestr, $i, 1);
$i += 1;
$n += 0.5;
}
}
if ($str_length > $cutlength) {
$returnstr .= $addstr;
}
return $returnstr;
}
這個函數增加了 $start 參數,可從指定位置開始截取:
function subUTF8str($str, $start = 0, $length = 80) {
$cur_len = 0;
$all_len = strlen($str);
if ($length > $all_len) return $str;
// 定位起始位置
for ($i = 0; $i < $all_len;) {
if ($cur_len == $start) break;
$i += (ord($str[$i]) > 127) ? 3 : 1;
$cur_len++;
}
$start_pos = $i;
// 截取指定長度
$temp_pos = $cur_len;
for (; $cur_len - $temp_pos < $length;) {
if ($i >= $all_len) break;
$i += (ord($str[$i]) > 127) ? 3 : 1;
$cur_len++;
}
$end_pos = $i;
return substr($str, $start_pos, $end_pos - $start_pos);
}
PHP 原生提供了多字節字符串處理函數族,這是最標準、最高效的方案:
mb_substr(string $str, int $start, int $length, string $encoding) - 字符串截取mb_strlen(string $str, string $encoding) - 獲取字符串長度mb_strcut、mb_strwidth 等輔助函數// 簡單易用,自動處理編碼
$str = "123這是測試字符串";
echo mb_substr($str, 0, 4, 'UTF-8'); // 輸出:123這
echo mb_substr($str, 0, 10, 'UTF-8'); // 輸出:123這是測試
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 自定義 ASCII 判斷 | 無需擴展,可自定義字符寬度邏輯 | 代碼複雜,需手動維護,效能一般 | 無法安裝擴展的受限環境 |
| subUTF8str | 支援起始位置截取 | 同樣需手動處理位元組邏輯 | 需要類似 substr 的完整功能時 |
| mbstring 擴展 | 官方標準、效能最佳、功能完整、無 Bug 風險 | 需伺服器安裝擴展 | 所有生產環境首選 |
mb_internal_encoding('UTF-8') 避免重複傳參ch 單位或 monospace 字體掌握正確的 UTF-8 字符串截取方法,能有效避免亂碼、截斷錯誤等常見問題,提升應用的國際化支援能力。