PHP 实现多字节字符串截取:兼容 UTF-8 与 GB2312 的常用函数方案
在处理中文网站内容列表或摘要生成时,直接截断字符串会导致 UTF-8 或 GB2312 编码不完整,进而引发乱码。本文提供多种经过验证的 PHP 字符串截取方案,涵盖自定义正则解析、基于 ASCII 码判断以及借鉴主流 CMS 系统的成熟算法,确保在中英文混排环境下能准确截取指定长度。
为什么需要专门的截取函数
PHP 原生 substr() 函数按字节切割,而 UTF-8 等编码中一个汉字通常占用 3 个字节。若直接截断,可能会切断汉字的中间字节,导致后续输出乱码或浏览器解析异常。因此,需根据目标字符集(Encoding)采用特定的逻辑来识别字符边界。
通用多编码截取函数 (UTF-8 & GB2312)
以下函数同时支持 UTF-8 和 GB2312 编码。其核心逻辑是利用正则表达式提取完整字符,或遍历字节并判断 ASCII 值以区分单字节字符与双字节汉字。
第1步:构建自适应编码的 cut_str 函数
目的:提供一个参数化接口,自动处理不同编码下的长度计算。
操作:将以下代码保存为工具文件。使用时指定 $code 参数。
关键参数:$string 为源文本,$sublen 为目标显示长度,$code 可选 'UTF-8' 或 'GB2312'。
<?php
function cut_str($string, $sublen, $start = 0, $code = 'UTF-8') {
if ($code == 'UTF-8') {
// 使用正则匹配 UTF-8 字符序列
$pa = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/";
preg_match_all($pa, $string, $t_string);
if (count($t_string[0]) - $start > $sublen) return join('', array_slice($t_string[0], $start, $sublen))."…";
return join('', array_slice($t_string[0], $start, $sublen));
} else {
// GB2312 处理:汉字占两字节,首字节大于129
$start = $start * 2;
$sublen = $sublen * 2;
$strlen = strlen($string);
$tmpstr = '';
for ($i = 0; $i < $strlen; $i++) {
if ($i >= $start && $i < ($start + $sublen)) {
if (ord(substr($string, $i, 1)) > 129) {
$tmpstr .= substr($string, $i, 2); // 取两个字节组成汉字
} else {
$tmpstr .= substr($string, $i, 1); // 取一个字节
}
}
if (ord(substr($string, $i, 1)) > 129) $i++; // 跳过汉字第二字节
}
if (strlen($tmpstr) < $strlen) $tmpstr .= "…";
return $tmpstr;
}
}
// 示例调用
echo cut_str("abcd需要截取的字符串", 8, 0, 'gb2312');
?>
针对特定编码的轻量级实现
若确定字符集固定,可使用更精简的正则或循环逻辑。
第2步:UTF-8 专用截取函数 utf8Substr
入口:纯 UTF-8 环境下的 API 或文章内容截取。
动作:利用 preg_replace 的非贪婪匹配特性,直接替换掉多余部分。
<?php
// 截取 utf8 字符串
function utf8Substr($str, $from, $len) {
return preg_replace('#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$from.'}'.
'((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$len.'}).*#s',
'$1', $str);
}
?>
第3步:GB2312 专用截取函数 mysubstr
操作:通过检查 ord() 返回值是否大于 0xa0 (160) 来判断是否为中文双字节字符。
<?php
function mysubstr($str, $start, $len) {
$tmpstr = "";
$strlen = $start + $len;
for ($i = 0; $i < $strlen; $i++) {
if (ord(substr($str, $i, 1)) > 0xa0) {
$tmpstr .= substr($str, $i, 2);
$i++; // 跳过下一个字节
} else {
$tmpstr .= substr($str, $i, 1);
}
}
return $tmpstr;
}
?>
借鉴开源项目的成熟算法
部分大型 CMS 系统内置了经过大量测试的截取函数,逻辑更为严谨,能处理特殊符号。
第4步:BugFree 风格的 sysSubStr
特点:根据高位字节范围(>=224, >=192, <192)判断 UTF-8 字符是 3 字节、2 字节还是 1 字节,兼容性较好。
<?php
function sysSubStr($String, $Length, $Append = false) {
if (strlen($String) <= $Length) return $String;
$I = 0;
while ($I < $Length) {
$StringTMP = substr($String, $I, 1);
if (ord($StringTMP) >= 224) {
$StringTMP = substr($String, $I, 3);
$I = $I + 3;
} elseif (ord($StringTMP) >= 192) {
$StringTMP = substr($String, $I, 2);
$I = $I + 2;
} else {
$I = $I + 1;
}
$StringLast[] = $StringTMP;
}
$StringLast = implode("", $StringLast);
if ($Append) $StringLast .= "…";
return $StringLast;
}
?>
第5步:DedeCMS 中的 cn_substr_utf8
注意:该函数兼容 MySQL 4.1 以下的 varchar 限制,按数据库字段长度逻辑截取。
<?php
function cn_substr_utf8($str, $length, $start=0) {
if (strlen($str) < $start+1) return '';
preg_match_all("/./su", $str, $ar);
$str = '';
$tstr = '';
for ($i=0; isset($ar[0][$i]); $i++) {
if (strlen($tstr) < $start) {
$tstr .= $ar[0][$i];
} else {
if (strlen($str) < $length + strlen($ar[0][$i]) ) {
$str .= $ar[0][$i];
} else {
break;
}
}
}
return $str;
}
?>
总结
选择字符串截取方案时,若项目统一使用 UTF-8,推荐优先使用 cut_str 或 sysSubStr,因为它们对边界字符的处理更为稳健。对于老旧 GB2312 系统,mysubstr 是最简单的替代方案。在使用任何截断函数时,建议在末尾追加省略号(如 `...` 或 `…`)以提升用户体验。
以上就是 PHP 字符串截取功能的详细内容,更多关于 PHP 字符编码处理的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













