在 PHP 早期版本或未安裝 mbstring 擴展的環境下,處理中文等多位元組編碼的字串截取是常見痛點。本文提供兩個相容 UTF-8 與 GB2312/Big5 的自訂函數,並說明核心邏輯與現代替代方案。
<?php
function ssubstr($string, $length, $dot = ' ...') {
global $charset; // 慣例由全域變數指定編碼:'utf-8'、'gb2312'、'big5'
if (strlen($string) <= $length) return $string;
// 預處理 HTML 實體
$string = str_replace(['&', '"', '<', '>'], ['&', '"', '<', '>'], $string);
$strcut = '';
if (strtolower($charset) == 'utf-8') {
// UTF-8 位元組序列判斷邏輯
$n = $tn = $noc = 0;
while ($n < strlen($string)) {
$t = ord($string[$n]);
if ($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) { // ASCII
$tn = 1; $n++; $noc++;
} elseif (194 <= $t && $t <= 223) { // 2 bytes
$tn = 2; $n += 2; $noc += 2;
} elseif (224 <= $t && $t <= 239) { // 3 bytes
$tn = 3; $n += 3; $noc += 2;
} elseif (240 <= $t && $t <= 247) { // 4 bytes
$tn = 4; $n += 4; $noc += 2;
} elseif (248 <= $t && $t <= 251) { // 5 bytes
$tn = 5; $n += 5; $noc += 2;
} elseif ($t == 252 || $t == 253) { // 6 bytes
$tn = 6; $n += 6; $noc += 2;
} else {
$n++;
}
if ($noc >= $length) break;
}
if ($noc > $length) $n -= $tn;
$strcut = substr($string, 0, $n);
} else {
// GB2312 / Big5:雙位元組編碼,高位元組 > 127
for ($i = 0; $i < $length; $i++) {
$strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
}
}
// 還原 HTML 實體
$strcut = str_replace(['&', '"', '<', '>'], ['&', '"', '<', '>'], $strcut);
return $strcut . $dot;
}
?>
<?php
function wordscut($string, $length, $suffix = true) {
if (strlen($string) <= $length) return $string;
if ($suffix) { $length -= 3; $addstr = '...'; } else { $addstr = ''; }
$wordscut = '';
for ($i = 0; $i < $length; $i++) {
if (ord($string[$i]) > 127) {
$wordscut .= $string[$i] . $string[$i + 1];
$i++;
} else {
$wordcut .= $string[$i];
}
}
return $wordscut . $addstr;
}
?>
| 編碼 | 判斷依據 | 中文字元位元組數 |
|---|---|---|
| UTF-8 | 首位元組範圍: 194-223(2B) 224-239(3B) 240-247(4B)... | 通常 3 位元組 |
| GB2312/Big5 | 首位元組 > 127(高位元組) | 固定 2 位元組 |
PHP 5.2+ 內建 mbstring 擴展(多數主機預設開啟),強烈建議直接使用:
<?php
// 確保腳本編碼與內部編碼一致
mb_internal_encoding('UTF-8');
$str = "這是一段測試字串,包含英文 abc 123。";
echo mb_substr($str, 0, 10, 'UTF-8') . '...'; // 這是一段測試字...
?>
<?php
function mstrlen($str, $charset = 'UTF-8') {
if (function_exists('mb_strlen')) return mb_strlen($str, $charset);
if (function_exists('iconv_strlen')) return iconv_strlen($str, $charset);
// 相容 fallback:正則計算 UTF-8 字元數
preg_match_all('/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3}/', $str, $ar);
return count($ar[0]);
}
?>
mb_substr()、mb_strlen(),設定 mb_internal_encoding('UTF-8')。ssubstr(),並確保 $charset 全域變數正確設定。LEFT()、SUBSTRING() 在多位元組編碼下同樣會按位元組截斷,建議應用層處理。