針對中英混雜字串,按「視覺寬度」(中文算 2、英文算 1)截取指定長度,自動處理 HTML 實體避免截斷,支援 UTF-8 與 GBK/Big5 編碼。
<?php
function cutstr($string, $length, $dot = ' ...') {
if (strlen($string) <= $length) return $string;
$pre = chr(1);
$end = chr(1);
// 保護 HTML 實體
$string = str_replace(
array('&', '"', '<', '>'),
array($pre . '&' . $end, $pre . '"' . $end, $pre . '<' . $end, $pre . '>' . $end),
$string
);
$strcut = '';
if (strtolower(CHARSET) == '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 { // GBK/Big5
for ($i = 0; $i < $length; $i++) {
$strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
}
}
// 還原 HTML 實體
$strcut = str_replace(
array($pre . '&' . $end, $pre . '"' . $end, $pre . '<' . $end, $pre . '>' . $end),
array('&', '"', '<', '>'),
$strcut
);
// 移除殘留保護標記
$pos = strrpos($strcut, chr(1));
if ($pos !== false) $strcut = substr($strcut, 0, $pos);
return $strcut . $dot;
}
?>
<?php
define('CHARSET', 'utf-8');
$str = "這是一個測試字串 test string <b>bold</b>";
echo cutstr($str, 20); // 輸出: 這是一個測試字串 test ...
?>
PHP 5.4+ 建議使用 mb_strimwidth():
echo mb_strimwidth($str, 0, 20, '...', 'UTF-8');
但需注意 `mb_strimwidth` 以字元數計算(中英皆算 1),視覺寬度控制仍需自訂函數。