PHP 实现多字节字符串截取:兼容 UTF-8 与 GB2312 的常用函数方案

七月
七月
发布时间:2026-10-16 | 4人浏览过
原创

在处理中文网站内容列表或摘要生成时,直接截断字符串会导致 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 字符编码处理的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表