处理中文或UTF-8多字节字符串必须用mb_substr()并显式传'UTF-8';substr()按字节截取会导致中文乱码,因一个UTF-8汉字占3字节,易截断字符中间;PHP无substring()函数,属常见误传。

直接说结论:处理中文或 UTF-8 多字节字符串,必须用 mb_substr() 并显式传 'UTF-8';纯英文/ASCII 场景可用 substr();别信 substring() —— PHP 根本没有这个内置函数,是常见误传。
为什么 substr() 截中文会乱码
substr() 按字节截取,而一个 UTF-8 中文占 3 字节。比如 "你好" 实际是 6 字节,substr($str, 0, 3) 只取前 3 字节,恰好卡在第一个汉字中间,输出就是乱码(如 好)。
常见错误现象:
- 页面显示 、、□ 等异常符号
- JSON 输出被截断导致解析失败
- 数据库写入时触发
Incorrect string value报错
解决办法只有两个条件同时满足:
立即学习“PHP免费学习笔记(深入)”;
- 启用
mbstring扩展(检查phpinfo()或运行extension_loaded('mbstring')) - 调用
mb_substr($str, $start, $length, 'UTF-8'),编码参数不能省略
mb_substr() 的 encoding 参数为什么不能省
省略第四个参数时,mb_substr() 依赖 mb_internal_encoding() 返回的值,而该值可能被其他代码临时修改,或未在 php.ini 中设为 UTF-8,导致行为不一致。
实操建议:
- 始终显式传
'UTF-8',哪怕本地测试正常 - 避免在函数外调用
mb_internal_encoding('UTF-8')来“一劳永逸”,它不是线程安全的 - 如果环境无法启用
mbstring(极少见),降级方案只能是先用iconv_strlen()判断长度,再用substr()配合字节偏移计算 —— 但强烈不推荐,复杂且易错
按分隔符截取时,别硬套 substr() + strpos()
例如想取邮箱 @ 前的部分,有人写 substr($email, 0, strpos($email, '@'))。这看似可行,但一旦 $email 不含 @,strpos() 返回 false,substr() 就会报 Warning 并返回空字符串。
更健壮的写法:
- 用
explode('@', $email)[0]——explode()对不存在的分隔符返回原字符串数组,不会崩溃 - 或加判断:
$pos = strpos($email, '@'); $user = $pos !== false ? substr($email, 0, $pos) : $email; - 若需兼容空格、换行等干扰,先
trim()再处理
生成摘要时加省略号的坑
很多人用 mb_substr($text, 0, 100, 'UTF-8') . '...',但这样可能在 HTML 标签中间截断,比如截出 <p>Hello...</p> 变成 <p>Hello...<,破坏 DOM 结构。
真实项目中要注意:
- 如果原文含 HTML,应先用
strip_tags()去标签再截取 - 若必须保留简单格式,可配合
mb_strrpos()找最后一个空格位置做软截断,避免单词被劈开 - 省略号本身占一个字符,
mb_strlen($text, 'UTF-8')计算时要预留空间
最麻烦的点其实不在函数选型,而在于你永远不知道上游数据是否真的 UTF-8 编码 —— 从数据库读、API 接、文件导入,都得先验码,否则 mb_substr() 也会失效。



















