php原生substr按字节截取utf-8中文易乱码,因中文占3字节;mb_substr需同时满足启用mbstring、显式传utf-8编码、字符串确为utf-8三条件才有效。

substr 按字节切,UTF-8 中文占 3 字节
PHP 原生 substr 函数不识别字符边界,只按字节计数。UTF-8 编码下,一个中文字符占 3 个字节,substr($str, 0, 10) 可能刚好在第 9、10、11 字节中间切断——比如把「好」字的 \xe5\xa5\xbd 拆成 \xe5\xa5 和 \xbd,后半截缺失,浏览器无法解析,就显示为 或空白。
常见错误现象包括:
- 前端显示“这是一”“测试内”“欢”这类不完整片段
- 截取长度越小(如
substr($str, 0, 5)),乱码概率越高 - 中英文混排时,同样长度下中文显示更少,视觉错位明显
mb_substr 不是开箱即用,缺一不可
mb_substr 能正确识别中文字符,但必须同时满足三个条件,缺一个就会退化回乱码:
- PHP 环境已启用
mbstring扩展(检查phpinfo()或运行extension_loaded('mbstring')) - 调用时显式传入编码参数,例如
mb_substr($str, 0, 10, 'UTF-8');漏掉第 4 个参数会依赖默认编码(可能不是 UTF-8) - 字符串本身确实是 UTF-8 编码——如果数据库字段是
gbk,却传'UTF-8',照样乱码
ThinkPHP 模板里用 substr 就是自找麻烦
ThinkPHP 模板引擎不会自动把 substr 替换为多字节版本,它直接调用 PHP 原生函数。你在模板里写 {:substr($title, 0, 20)},和在普通 PHP 文件里写完全一样,毫无例外。
安全做法只有两个:
- 在控制器层提前用
mb_substr处理好再 assign 给模板 - 在模板里改用
{:mb_substr($title, 0, 20, 'UTF-8')},但前提是确保mbstring已启用且编码匹配
别去魔改框架源码或手写正则截取——性能差、边界 case 多、维护成本高。
混排字符串(中/英/数字/emoji)要特别小心
emoji 是四字节 UTF-8 字符(如 ? = \xf0\x9f\x8c\x8d),mb_substr 虽能处理,但必须确认 PHP 版本 ≥ 7.2 且 mbstring.func_overload 未开启(该配置已废弃,但旧环境可能残留)。否则 mb_strlen 可能误判 emoji 长度,导致截断异常。
验证方式很简单:
- 用
mb_strlen('a?中', 'UTF-8')应返回3 - 若返回
4或5,说明环境有问题,得查mbstring配置或升级 PHP
真正容易被忽略的是:编码声明和实际数据不一致。比如页面声明了 charset=UTF-8,但 MySQL 连接用的是 SET NAMES gbk,这时无论怎么调 mb_substr 都救不回来。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











