必须用 mb_substr 而非 substr 处理中文等多字节字符,因 substr 按字节截取易导致乱码;mb_substr 需显式指定 'UTF-8' 编码,且注意 mbstring 扩展启用、性能陷阱及 mb_strcut 的字节安全截取用途。

用 substr 处理中文、日文、emoji 等多字节字符,大概率会出乱码;必须用 mb_substr 才能按“字符”正确截取。
为什么 substr 一碰中文就错
substr 完全不关心编码,只按字节位置切。UTF-8 中一个中文字符占 3 个字节,substr("你好", 0, 2) 会切出前 2 个字节——这不是完整字符,浏览器显示就是 或乱码。
常见错误现象:
-
substr("测试abc", 0, 4)返回"测"(不是“测试”,也不是“测试a”) - 数据库字段被
substr截断后写入,查出来显示异常或报错Invalid UTF-8 sequence - API 返回 JSON 里字段值出现 符号,前端解析失败
mb_substr 的参数和编码必须配对
mb_substr 按字符数截,但前提是它得知道当前字符串用的是什么编码。否则它可能把 UTF-8 当成 GBK 数,结果还是错。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 显式传入
'UTF-8',不要依赖默认编码:mb_substr($str, 0, 10, 'UTF-8') - 确认 PHP 已启用
mbstring扩展(php -m | grep mbstring),否则调用直接报Call to undefined function - 如果源字符串编码不确定,先用
mb_detect_encoding($str)探测,再传给mb_substr(注意该函数不可靠,仅作兜底) -
$length可为负数:例如mb_substr($str, 0, -2, 'UTF-8')表示去掉末尾 2 个字符
mb_substr 不是万能的,小心性能陷阱
mb_substr 每次调用都要扫描字符串找字符边界,对长文本反复单字符遍历(比如循环 mb_substr($str, $i, 1))会导致 O(n²) 时间复杂度。
更高效的做法:
- 用
mb_str_split($str, 1, 'UTF-8')一次性拆成字符数组(PHP 7.4+) - 若只需前 N 字符,直接
mb_substr($str, 0, $n, 'UTF-8'),别用循环 - 对超长日志或用户输入做预处理时,先用
mb_strlen($str, 'UTF-8')判断长度,避免无效截取
别漏掉 mb_strcut 这个替代选项
当你要控制最终输出的**字节数**(比如 HTTP 响应限制 1024 字节),而不是字符数,mb_strcut 更合适——它按字节截但保证不切断多字节字符,不会产生半个汉字。
对比示例:
echo mb_substr("我爱PHP", 0, 4, 'UTF-8'); // 输出:"我爱PH"(4 个字符)
echo mb_strcut("我爱PHP", 0, 4, 'UTF-8'); // 输出:"我"(UTF-8 下"我"占 3 字节,第 4 字节不够下一个字符)
真正容易被忽略的是:很多人以为只要用了 mb_* 函数就万事大吉,却没意识到 encoding 参数漏传、扩展未开启、或误把 mb_strcut 当 mb_substr 用,结果在边界 case 上突然出问题。



















