substr按字节截中文必乱码,因utf-8中文占3字节;mb_substr必须显式指定'utf-8'编码,否则易错;mb_strcut按字节截取,仅适用于二进制场景;负索引需先用mb_strlen确认字符数,且须确保mbstring扩展已启用。

直接用 substr() 截中文,乱码是必然的——它根本不管字符边界,只按字节硬切。UTF-8里一个中文占3字节,substr("你好", 0, 2) 切出来就是两个残缺字节,浏览器一渲染就显示或一堆问号。
mb_substr() 必须指定 encoding 参数
很多人写了 mb_substr($str, 0, 10) 还是乱码,问题就出在没传编码。PHP 的内部编码(mb_internal_encoding())不一定等于你字符串的实际编码,尤其当数据来自数据库、API 或表单时。
- 显式传
'UTF-8'最稳妥,99% 的现代 PHP 项目都用这个 - 别依赖默认值:如果
mb_internal_encoding()是ISO-8859-1,而你传的是 UTF-8 字符串,结果照样错 - 用
mb_detect_encoding($str, ['UTF-8', 'GB2312', 'BIG5'], true)做兜底检测,但别在高频路径里用,性能差
mb_substr 和 mb_strcut 的行为差异很关键
这两个函数名字像、参数一样,但底层逻辑完全不同,选错会出线上问题。
-
mb_substr()数“字符”:你写mb_substr($str, 0, 5, 'UTF-8'),就一定得 5 个完整字符,哪怕它们总共占 15 字节 -
mb_strcut()数“字节”:你写mb_strcut($str, 0, 5, 'UTF-8'),它从第 0 字节开始拿 5 字节,如果第 4~5 字节正好卡在一个中文字符中间,就返回"你好?"这类带替代符的结果 - 做摘要、前端展示、用户可见内容,必须用
mb_substr();只有处理二进制流、协议包、或明确要控制字节数(比如 Redis key 长度限制)才考虑mb_strcut()
常见错误:负数索引 + 中文混排时行为反直觉
mb_substr($str, -2, 1) 看似“取倒数第二个字符”,但实际行为取决于编码和字符串组成。例如:
$str = "a你好b"; // ASCII + UTF-8 混合 echo mb_substr($str, -2, 1, 'UTF-8'); // 输出 "好"(不是 "b")
因为 mb_substr 是按字符位置算的:"a你好b" 共 5 个字符,索引是 0→a, 1→你, 2→好, 3→b;-2 就是索引 3,所以取到的是 "b"?不对——等等,再数一遍:a(0)、你(1)、好(2)、b(3),共 4 个字符。那 -2 就是索引 2 → “好”。
- 混排字符串中,永远先用
mb_strlen($str, 'UTF-8')确认总字符数,再算负索引对应的位置 - 避免在动态长度字符串上直接写
-n,尤其当内容来自不可信输入时(比如用户昵称含 emoji,一个 emoji 可能占 4 字节、算 1 字符) - 如果真要“取末尾 N 个字符”,写成
mb_substr($str, -N, null, 'UTF-8')比mb_substr($str, -N, N, 'UTF-8')更安全,省得自己算错
最常被忽略的一点:mbstring 扩展不是默认启用的。上线前不检查 extension=mbstring 是否在 php.ini 里生效,或者没确认 Docker 镜像/云函数运行时是否预装,mb_substr() 直接报 Call to undefined function —— 这种错误不会在本地开发环境暴露,专等发布后炸。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











