PHP截断含西里尔与拉丁混合字符串时末尾出现乱码的解决方法
乱码根因说明
- 你遇到的截断后出现
�、换多字节函数后问题反转、混排标题优先出问题的现象,是两个逻辑漏洞叠加导致的,不是mb系列函数本身的问题:- 正则匹配逻辑没有启用多字节感知:原代码中使用的
preg_match("/^(.*)\W.*$/", ...)没有加正则的UTF-8匹配修饰符u,默认会把字符串按单字节流处理,对双字节的西里尔字母、单字节的拉丁字母混排内容,很容易从字符中间拆出半截字节,这些不完整的字节序列在页面渲染时就会被替换成�。 - 截断范围和正则的匹配误差被放大:之前用单字节
strlen/substr的时候,西里尔字符按2字节计数,实际截断出来的字符数比设定值少一半,离容易触发字符拆分的边界远,所以问题不明显;换成mb系列函数后,长度计数精准到单个字符,刚好碰到混排的匹配边界,又因为正则没做多字节适配,之前被掩盖的拆字符问题就暴露了。
- 正则匹配逻辑没有启用多字节感知:原代码中使用的
可落地修复方案
- 修正全流程的多字节适配逻辑,从字符截断到正则匹配全部对齐UTF-8编码规则,可直接替换为以下代码:
function the_title_limit($length, $replacer = "...") { // 自动读取站点设置的编码,避免硬编码错配 $charset = get_bloginfo('charset') ?: 'UTF-8'; $string = the_title("", "", false); // 长度未超限直接输出 if (mb_strlen($string, $charset) <= $length) { echo $string; return; } // 按目标长度多字节截断 $trim_part = mb_substr($string, 0, $length + 1, $charset); // 正则加u修饰符,按多字节字符匹配词边界 if (preg_match('/^(.*)\W.*$/u', $trim_part, $matches)) { $string = $matches[1]; } else { // 找不到词边界直接硬截断到目标长度 $string = mb_substr($string, 0, $length, $charset); } // 兜底清理可能残留的不完整字节,杜绝乱码 $string = iconv($charset, $charset . '//IGNORE', $string) ?: $string; echo $string . $replacer; }
- 额外的高度一致兜底技巧:
- PHP截断只做长度参考,配合CSS给标题容器设置
overflow: hidden、word-break: break-word,就算有个别截断不齐,也不会撑破文章展示框的统一高度。 - 不需要为不同长度调用值单独调整逻辑,只要正则的多字节适配正确,不管传100、101还是102的长度参数,都不会出现拆字符出乱码的问题。
- PHP截断只做长度参考,配合CSS给标题容器设置
内容的提问来源于stack exchange,提问作者Todor Atanasov
相关产品推荐
相关产品推荐

