PHP字符串截取函数对比:Discuz、Ecshop及优化版实现

Miles
Miles
发布时间:2026-10-15 | 3人浏览过
原创

在Web开发中,UTF-8编码下的字符串截断是常见需求。不同框架对中英文长度的定义不同,直接使用原生函数容易导致截断不完整或显示异常。本文通过对比Discuz和Ecshop的实现逻辑,提供兼顾性能与兼容性的优化方案。

结果与目标

在处理包含中文、英文混合的长文本时,往往需要将内容截取为固定长度并添加省略号。核心难点在于确定“长度”的定义:是按字节数计算,还是按视觉字符数计算?尤其是当需要让一个中文字符占据两个位置(类似GBK编码习惯)时,算法实现变得复杂。以下将对比两种主流方案及其潜在问题。

准备条件

  • 编码环境:主要讨论UTF-8编码,这是目前网页的标准编码格式。
  • 应用场景:列表页标题截断、简介生成等需控制显示宽度的场景。
  • 核心痛点:原生substr()在中文字符边界截断会产生乱码;纯字节计数无法直观对应“字数”。

真实操作顺序

第1步:理解Discuz版本的实现逻辑

目的:在不依赖扩展库的情况下,手动遍历字符串以模拟长度计算。 原理:该版本试图通过逐字节判断ASCII码范围来识别字符类型。其关键策略是将中文字符计为2个单位,英文字母计为1个单位。

代码特点:

  • 使用ord()获取字节值,通过位运算判断字符类型(如$t == 9 || $t == 10判断换行)。
  • 通过$pre = chr(1)等占位符保护HTML实体字符,防止被错误截断。
  • 缺陷分析:strlen()获取的是字节总长,但与逻辑计数器$noc(累计宽度)进行直接比较存在逻辑偏差。例如5个中文汉字,字节长度为15,但逻辑宽度为10。若请求截取宽度10,函数可能在内部逻辑溢出后仍追加省略号,导致显示重复或多余字符。
复制代码 代码如下:
/**
 * Discuz风格截取:中文字符按2个字符宽度计算
 * @param string $string 源字符串
 * @param int $length 目标逻辑长度
 * @param string $dot 尾部填充
 */
function cutstr($string, $length, $dot = '...') {
    if (strlen($string) <= $length) return $string;

    // 简单的HTML实体转义保护逻辑(伪代码示意)
    // ... 替换 & lt; gt; for special chars ...

    $n = $tn = $noc = 0;
    while ($n < strlen($string)) {
        $t = ord($string[$n]);
        // 英文/数字/符号
        if ($t == 9 || $t == 10 || ($t >= 32 && $t <= 126)) { $tn = 1; $n++; $noc++;
        // 双字节(通常用于Latin extended或其他)
        } elseif ($t >= 194 && $t <= 223) { $tn = 2; $n += 2; $noc += 2;
        // 三字节 (UTF-8 CJK)
        } elseif ($t >= 224 && $t <= 239) { $tn = 3; $n += 3; $noc += 2;
        // 四字节及以上,按2计
        } else { $tn = ($t >= 240 ? 4 : 3); $n += $tn; $noc += 2; }
        
        if ($noc >= $length) break;
    }
    
    if ($noc > $length) $n -= $tn;
    $strcut = substr($string, 0, $n);
    
    // 还原HTML实体
    // ... 反向替换 ...
    
    return $strcut . $dot;
}

第2步:理解Ecshop版本的实现逻辑

目的:利用PHP内置的多字节处理函数,简化代码并确保安全性。 原理:优先尝试mb_substr,若无则降级至iconv_substr。此版本严格按照字符数量截取,即“中文字算1,英文字母算1”。

优点:代码简洁,不易出错。 缺点:不符合传统排版中“全角/半角”混排的习惯。如果前端CSS设定一个中文占位宽度等于两个英文宽度,该函数会导致截断长度偏短(视觉上只截取了一半的中文空间)。

复制代码 代码如下:
function sub_str($str, $length = 0, $append = '...') {
    $str = trim($str);
    $strlength = strlen($str);
    if ($length == 0 || $length >= $strlength) return $str;
    
    // 核心:使用扩展库精确截取字符
    if (function_exists('mb_substr')) {
        $newstr = mb_substr($str, 0, $length, 'utf-8');
    } elseif (function_exists('iconv_substr')) {
        $newstr = iconv_substr($str, 0, $length, 'utf-8');
    } else {
        // 无扩展库时的不安全回退
        $newstr = substr($str, 0, $length);
    }
    
    if ($append && $str != $newstr) {
        $newstr .= $append;
    }
    return $newstr;
}

第3步:应用优化版函数(GBK转换法)

目的:解决“中文宽、英文窄”且无扩展库限制的问题。 原理:利用PHP的iconv扩展,临时将UTF-8字符串转为GBK编码。在GBK中,所有中文字符均为双字节,英文单字节。此时直接使用基于字节的循环即可实现“中文算2位,英文算1位”,最后再转回UTF-8。

优势:不依赖mbstring扩展(仅依赖iconv),算法逻辑清晰,性能优于手动正则匹配。

复制代码 代码如下:
/**
 * 通用优化版:支持UTF-8,中文按2字符算
 * @param string $string
 * @param int $length
 * @param bool|string $append
 */
function substring($string, $length, $append = false) {
    if ($length <= 0) return '';
    
    // 检测并转换为GBK以便统一字节计数
    $is_utf8 = true;
    $str_gbk = @iconv("UTF-8", "GBK", $string);
    $back_to_utf = @iconv("GBK", "UTF-8", $str_gbk);
    
    if ($string != $back_to_utf) {
        // 非UTF-8或非标准编码,简单处理
        return substr($string, 0, $length) . ($append ? '...' : '');
    }
    
    // 此时使用GBK字符串进行截取
    $string = $str_gbk;
    $newstr = '';
    
    for ($i = 0; $i < $length; $i++) {
        // 如果当前字节最高位为1(属于双字节字符的一部分)
        if (ord($string[$i]) > 127) {
            $newstr .= $string[$i] . $string[++$i]; // 跳过下一字节
        } else {
            $newstr .= $string[$i];
        }
    }
    
    // 转回UTF-8
    if ($is_utf8) {
        $newstr = iconv("GBK", "UTF-8", $newstr);
    }
    
    if ($append && $newstr != $string) {
        $newstr .= $append;
    }
    return $newstr;
}

测试验证:

  • 输入“白日依山尽”,截取4位:输出“白日...”(2个中文=4位)。符合预期。
  • 输入“12白34日”,截取4位:输出“12白...”(2个英文+1个中文=4位)。符合预期。

完成判断

通过对比发现,Ecshop方案适合严格字数统计场景,Discuz方案适合视觉宽度适配但代码冗余,而优化版脚本利用编码转换技巧,在无需mbstring扩展的情况下,实现了精准的“中文双倍宽”截断效果,是目前中小型项目中较优的解决方案。

简要总结

以上就是PHP中字符串截取函数的对比分析与优化的详细内容。在处理多语言混合内容时,选择正确的截取算法能有效避免乱码和排版错乱。更多关于PHP字符串操作的资料请关注本站其它相关文章!

本站声明

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

Python 相关产品和应用

全部下载

专题列表