必须用mb_substr($str, $start, $length, 'UTF-8')截取中文,第四参数不可省略;需检测末尾字符边界避免半截字;加省略号应手动拼接并预留长度;性能敏感时优先mb_substr,禁用func_overload;Windows下须检查mbstring扩展是否存在。

用 mb_substr() 截取中文不乱码
PHP 默认的 substr() 按字节截取,对 UTF-8 中文会切开多字节字符,导致乱码或 。必须用多字节安全函数。
关键点:指定编码参数,否则在某些环境(如旧版 PHP 或未配置默认编码时)仍可能出错。
-
mb_substr($str, $start, $length, 'UTF-8')—— 第四参数不能省略,尤其部署到不同服务器时 - 若不确定源字符串编码,先用
mb_detect_encoding($str)探测,但该函数不可靠,建议源头统一用 UTF-8 - 注意
mb_internal_encoding()只影响部分函数,默认值未必是 UTF-8,不要依赖它代替显式传参
截取后保留完整汉字/符号,避免“半截字”
直接按长度截容易在标点、emoji 或中英文混排处断开。比如截 10 个字符,结果末尾是半个笑脸或半个顿号。
解决思路不是“智能语义”,而是用边界检测做微调:
立即学习“PHP免费学习笔记(深入)”;
- 先用
mb_substr($str, 0, $max_len, 'UTF-8')获取初稿 - 再用
mb_strlen()检查末尾是否为合法字符边界:对常见情况,可检查最后 1~2 字符是否属于标点(如。!?;:""''()【】《》)或空白,若是,用mb_substr()向前收缩 1~2 位 - 更稳妥的做法是结合正则:用
mb_ereg_replace('^(.{0,'.$max_len.'})(?:[^[:punct:][:space:]]|$).*', '\1', $str),但性能略低,仅在展示层精度要求高时启用
mb_strimwidth() 快速加省略号,但要注意宽度陷阱
这个函数看起来方便:mb_strimwidth($str, 0, 20, '...', 'UTF-8'),但它计算的是“显示宽度”,而 PHP 的实现里,一个中文字符算 2 单位宽度,英文字母/数字算 1 —— 这和终端、CSS、前端渲染实际宽度并不一致。
后果:同样 20 宽度限制,在纯中文串里只截 10 字,在纯英文串里却能截 20 字,导致前端布局错乱。
- 除非你明确按“等宽字体渲染”设计(如后台日志预览),否则别用它控制前端展示长度
- 想加省略号,更可控的方式是:先
mb_substr()截取,再手动拼接'...',并用mb_strlen()确保总长 ≤ 目标字符数(非像素宽度) - 注意:省略号本身占 1 个 Unicode 字符(
…)或 3 字节(...),要从原长度里预留出来
超长字符串截取性能敏感时怎么选函数
当处理日志、用户评论等可能上万字符的文本时,反复调用 mb_substr() 或正则会明显拖慢响应。
优先级顺序很明确:
- 如果确定字符串一定是 UTF-8 且不含代理对(即无 emoji 或生僻汉字),可用
substr()+utf8_decode()伪方案(不推荐,兼容性差) - 真正可靠又快的是:用
mb_substr(),但确保mbstring.func_overload未开启(开启后会覆盖原生函数,反而变慢) - 极端场景(如 API 批量处理)可提前用
mb_strcut()替代 —— 它不识别字符边界,只按字节切但保证不拆 UTF-8 字符,比mb_substr()略快,适合“截断存档”类需求,但无法指定字符数
最常被忽略的一点:所有 mb_* 函数在 Windows 下若没启用 mbstring 扩展,会静默失败返回空字符串,而不是报错 —— 务必在上线前用 function_exists('mb_substr') 做兜底判断。



















