GBK/GB2312 編碼下,中文字符佔 2 字節,英文數字佔 1 字節。直接用 substr($str, 0, $len) 按字節截取,極易在雙字節字符中間斷開,產生亂碼(如「中」)。
smssubstr()<?php
/**
* 按顯示寬度(字節長度)截取混合編碼字符串
* @param string $string 源字符串
* @param int $length 目標字節長度(中文算 2,英文算 1)
* @return string 截取後字符串,不會產生半個漢字
*/
function smssubstr($string, $length) {
if (strlen($string) <= $length) return $string;
$result = '';
for ($i = 0; $i < $length; $i++) {
// ord > 127 視為雙字節字符首字節
if (ord($string[$i]) > 127) {
$result .= $string[$i] . $string[++$i]; // 連帶取下一字節
} else {
$result .= $string[$i];
}
}
return $result;
}
// 示例:將長短信按 70 字節分段
$message = '親愛的用戶,您的驗證碼是 123456,請在 5 分鐘內輸入。';
$smsper = 70; // 單條短信字節上限
$smsnum = ceil(strlen($message) / $smsper);
for ($i = 1; $i <= $smsnum; $i++) {
${'smscontent' . $i} = smssubstr($message, $smsper);
$message = str_replace(${'smscontent' . $i}, '', $message);
}
// 現在 $smscontent1, $smscontent2... 即為分段後的短信內容
?>
字符串: 中 國 a b 中 字節: D6 D0 B9 FA 61 62 D6 D0 索引: 0 1 2 3 4 5 6 7 length=5 截取過程: i=0 ord(D6)>127 → 取 D6 D0 (i變1) result="中" i=2 ord(B9)>127 → 取 B9 FA (i變3) result="中國" i=4 ord(61)≤127 → 取 61 result="中國a" i=5 ord(62)≤127 → 取 62 result="中國ab" 循環結束,完美避開半個漢字
若項目已全面 UTF-8,請直接使用 mb_substr():
mb_internal_encoding('UTF-8');
echo mb_substr($str, 0, 20, 'UTF-8'); // 按字符數截取,天然支援多語言
或 mb_strimwidth($str, 0, 70, '...', 'UTF-8') 一步到位補省略號。
header('Content-Type: text/html; charset=UTF-8');SET NAMES utf8mb4。