PHP 內建的 substr() 是按位元組切割,英文字佔 1 位元組,但在 UTF-8 編碼下,一個中文字佔 3 位元組。直接使用 substr() 會導致多位元組字元被切斷,產生亂碼。本文提供一個可正確按「字元寬度」截取的函數 truncate_utf8_string(),並解析其核心邏輯。
function truncate_utf8_string($string, $length, $etc = '...') {
$result = '';
// 先解碼 HTML 實體、去除標籤、修剪空白
$string = html_entity_decode(trim(strip_tags($string)), ENT_QUOTES, 'UTF-8');
$strlen = strlen($string);
for ($i = 0; ($i < $strlen) && ($length > 0); $i++) {
// 取得目前位元組的二進位表示,判斷 UTF-8 位元組數
$bin = str_pad(decbin(ord(substr($string, $i, 1))), 8, '0', STR_PAD_LEFT);
$number = strpos($bin, '0'); // 第一個 0 的位置即為位元組數
if ($number) { // 多位元組字元
if ($length < 1.0) break;
$result .= substr($string, $i, $number);
$length -= 1.0; // 視為 1 個字元寬度
$i += $number - 1; // 跳過剩餘位元組
} else { // 單位元組字元(ASCII)
$result .= substr($string, $i, 1);
$length -= 0.5; // 視為 0.5 個字元寬度(半形)
}
}
$result = htmlspecialchars($result, ENT_QUOTES, 'UTF-8');
if ($i < $strlen) $result .= $etc;
return $result;
}
<?php $str = "如果需要截取utf-8格式的字符串,直接調用這個函數即可。"; echo truncate_utf8_string($str, 10); // 輸出:如果需要截取utf-8格... ?>
ord() 取得位元組值,轉二進位後找第一個 0 的索引,即可知道該字元佔幾個位元組(UTF-8 編碼規則)。htmlspecialchars() 轉義,防止 XSS。mbstring 擴充,優先考慮 mb_substr() 或 mb_strimwidth(),效能更好且支援更多編碼。grapheme_substr()(需 intl 擴充)。本函數在無 mbstring 環境下,提供了一個純 PHP 實作的 UTF-8 安全截取方案,適合舊版專案或受限環境使用。現代專案建議優先使用多位元組字串函式庫。