在 GB2312/GBK 編碼下,英文字符佔 1 字節(半角,ASCII < 128),中文字符佔 2 字節(全角,高位字節 > 128)。若直接用 substr() 按字節截取,極易將一個漢字「切成兩半」,導致頁面顯示亂碼()。
遍歷字符串前 $length 字節,統計高位字節(> 128)的個數:
<?php
/**
* 安全截取 GB2312/GBK 編碼字符串
* @param string $str 輸入字符串
* @param int $len 期望顯示長度(按字節計)
* @param string $suffix 截斷後綴,默認 "..."
* @return string
*/
function chgTitle($str, $len = 46, $suffix = '...') {
if (strlen($str) <= $len) return $str;
$highBytes = 0;
for ($i = 0; $i < $len; $i++) {
if (ord($str[$i]) > 128) $highBytes++;
}
// 奇數說明切在漢字中間,向後補一位
$cutLen = ($highBytes % 2 === 0) ? $len : $len + 1;
return substr($str, 0, $cutLen) . $suffix;
}
// 測試
$title = "這是一個測試標題Test123";
echo chgTitle($title, 10); // 輸出:這是一個測試...
echo chgTitle($title, 11); // 輸出:這是一個測試標...
?>
// 假設 $str = "中A文" (GBK: D6 D0 41 CE C4)
// 字節索引: 0 1 2 3 4
// ord: D6 D0 41 CE C4
// >128: 是 是 否 是 是
// 截取前 3 字節 (len=3)
// 高位計數 = 2 (偶數) -> 直接截取 -> "中A" (正確)
// 截取前 4 字節 (len=4)
// 高位計數 = 3 (奇數) -> 補 1 -> 取 5 字節 -> "中A文" (正確)
// 若不補位直接取 4 字節 -> "中A文" 的前半個 "文" -> 亂碼
mb_substr 方案現代項目建議統一使用 UTF-8 編碼,並直接用多字節字符串函數:
<?php
// PHP 內置,正確按字符而非字節計數
$str = "這是測試Test";
echo mb_substr($str, 0, 6, 'UTF-8') . '...'; // 輸出:這是測試T...
// 封裝通用函數(自動檢測編碼或指定 UTF-8)
function mb_chgTitle($str, $len = 46, $suffix = '...', $encoding = 'UTF-8') {
if (mb_strlen($str, $encoding) <= $len) return $str;
return mb_substr($str, 0, $len, $encoding) . $suffix;
}
?>
| 編碼環境 | 推薦函數 | 關鍵點 |
|---|---|---|
| GB2312/GBK (舊項目) | 自定義 chgTitle() | 統計高位字節奇偶,補位湊齊雙字節 |
| UTF-8 (新項目) | mb_substr() / mb_strimwidth() | 按字符計數,無需手動處理字節邊界 |
最佳建議:新項目全棧使用 UTF-8(數據庫、頁面、PHP 文件編碼),徹底避開字節截取陷阱。