PHP 字串操作預設按位元組處理,直接使用 substr() 截取 UTF-8 中文會導致字元截半產生亂碼。正確做法是使用 mb_substr() 或自訂函數按字元計算長度。
<?php
// 原始函數:嘗試判斷 ASCII/多位元組字元,但邏輯有誤
function entities_rtrim($string, $length) {
if ($length >= strlen($string)) return $string;
$s = '';
for ($i = 0; $i < $length; $i++) {
if (ord($string[$i]) > 127) {
$s .= $string[$i] . $string[$i]; // 錯誤:重複加入同一位元組
continue;
} else {
$s .= $string[$i];
continue;
}
}
return $s;
}
?>
此函數有嚴重缺陷:ord($string[$i]) > 127 只能偵測多位元組字元的第一個位元組,且 $string[$i] . $string[$i] 重複寫入同一位元組,無法正確組合完整字元。
<?php
// 確保 php.ini 啟用 mbstring 擴充套件
mb_internal_encoding('UTF-8');
function mb_trim_string($string, $length, $suffix = '...') {
if (mb_strlen($string) <= $length) return $string;
return mb_substr($string, 0, $length) . $suffix;
}
echo mb_trim_string('你好世界,PHP 程式設計', 10); // 你好世界,PHP...
?>
<?php
function utf8_trim_string($string, $length, $suffix = '...') {
$len = strlen($string);
if ($len <= $length) return $string;
$charCount = 0;
$bytePos = 0;
while ($bytePos < $len && $charCount < $length) {
$byte = ord($string[$bytePos]);
if ($byte < 0x80) { // 1-byte (ASCII)
$bytePos += 1;
} elseif ($byte < 0xE0) { // 2-byte
$bytePos += 2;
} elseif ($byte < 0xF0) { // 3-byte (常見中日韓)
$bytePos += 3;
} elseif ($byte < 0xF8) { // 4-byte (Emoji 等)
$bytePos += 4;
} else {
$bytePos += 1; // 無效位元組,跳過
}
$charCount++;
}
return substr($string, 0, $bytePos) . $suffix;
}
echo utf8_trim_string('你好世界😀', 5); // 你好世界...
?>
<?php
function regex_trim($string, $length, $suffix = '...') {
if (mb_strlen($string) <= $length) return $string;
// 符合前 $length 個字元(含多位元組)
preg_match('/^.{' . $length . '}/u', $string, $matches);
return $matches[0] . $suffix;
}
?>
| 方法 | 相對速度 | 備註 |
|---|---|---|
| mb_substr | 1.0x (基準) | C 層實作,最快且正確 |
| 自訂 UTF-8 解析 | ~3-5x 慢 | 純 PHP 迴圈,無 mbstring 時備用 |
| preg_match /u | ~10-20x 慢 | 正則引擎開銷大,不建議高頻使用 |
mb_substr():確保伺服器安裝 php-mbstring。mb_internal_encoding('UTF-8') 或 php.ini: mbstring.internal_encoding = UTF-8。LEFT(str, len) 按字元截取,減少傳輸資料量。text-overflow: ellipsis 處理顯示層截斷,保留完整資料給 SEO/無障礙存取。正確處理多位元組字串截取是 PHP 開發中文系統的基本功,請務必養成使用 mb_* 函數族群的習慣。