用 mb_substr() 替代 substr() 是唯一靠谱做法,因其按字符而非字节截取,避免 utf-8 中文乱码;务必显式指定编码 'utf-8',并确保源头数据(如数据库连接、表单提交)编码一致。

用 mb_substr() 替代 substr() 是唯一靠谱做法
PHP 默认的 substr() 按字节切,中文 UTF-8 下一个字占 3 字节,直接切会截断中间字节,产生乱码或 。必须用多字节函数。
实操建议:
-
mb_substr()第一个参数是字符串,第二个是起始位置(按字符数,不是字节),第三个是长度(也是字符数) - 务必显式传入编码:
mb_substr($str, 0, 10, 'UTF-8'),不传可能依赖mb_internal_encoding(),线上环境容易不一致 - 如果源字符串编码不确定,先用
mb_detect_encoding()判断,但该函数不可靠,更稳的做法是统一转成 UTF-8 再处理
截取前要确认字符串真实编码,别信 $_POST 或数据库字段声明
常见错误现象:数据库字段设了 utf8mb4,但 PHP 连接没设 charset;或前端表单没声明 accept-charset="UTF-8",导致接收的中文其实是 GBK 编码。这时用 mb_substr(..., 'UTF-8') 仍会乱码。
使用场景:
立即学习“PHP免费学习笔记(深入)”;
- 从 MySQL 读数据后,检查是否真为 UTF-8:
mb_check_encoding($str, 'UTF-8')返回false就得转码 - 转码用
mb_convert_encoding($str, 'UTF-8', 'GBK'),第二个参数是目标编码,第三个是源编码,顺序错就全乱 - 用
mb_list_encodings()查看当前支持哪些编码,避免拼错如写成'utf8'(少横线)——PHP 里'UTF-8'才标准
用 mb_strimwidth() 做带省略号的中文截断更安全
需要显示“张三丰…”,而不是“张三丰”后面突然断掉?mb_substr() 自己拼 ... 容易超长,因为省略号也算一个字符。这时候用 mb_strimwidth() 更省心。
参数差异:
-
mb_strimwidth($str, 0, 20, '...', 'UTF-8')—— 第三个参数是「总宽度(字符数)」,包括省略号本身 - 注意:省略号
'...'是三个 ASCII 字符,不算一个 Unicode 字符,所以如果限制 20 字符,最多显示 17 个中文 + 3 个点 - 如果省略号想用中文省略号
'……'(两个全角点),它占 2 字符,那实际正文只剩 18 字符空间
性能和兼容性:别在循环里反复调用 mb_internal_encoding()
有人为了图省事,在函数开头写 mb_internal_encoding('UTF-8'),再用 mb_substr() 不带编码参数。这在 CLI 脚本里可能没问题,但在 Web SAPI(如 FPM)下,这个设置是进程级的,可能被其他请求改写,造成并发乱码。
实操建议:
- 永远在每个
mb_*函数调用里显式传'UTF-8',不依赖全局状态 - 如果大量使用,可封装一层:
function mb_sub($s, $start, $len) { return mb_substr($s, $start, $len, 'UTF-8'); },避免重复写编码 - PHP 8.2+ 开始,
mb_internal_encoding()在 Web 环境已被标记为 deprecated,未来会彻底禁用
最常被忽略的点:数据库连接层的编码设置比 PHP 字符串函数更容易出问题。哪怕 mb_substr() 用得再对,源头数据已经是乱码,一切白搭。











