PHP中判断字符串截取后长度,必须用mb_substr()和mb_strlen()配合并显式指定UTF-8编码,因substr()和strlen()按字节操作,对中文等多字节字符会乱码或误判长度。

PHP中用substr()截取字符串,看似简单,但一碰中文、Emoji或特殊编码就容易出错。关键不是函数不会用,而是没分清“字节”和“字符”的区别。
substr()基础用法:按字节操作
substr()只认字节,不认字符。它不管你是英文、中文还是表情符号,一律按原始字节流切。
- 语法:
substr(string $string, int $start, ?int $length = null) -
$start为非负数时,从开头第
$start个字节开始(索引从0起) -
$start为负数时,从末尾倒数第
abs($start)个字节开始 - $length为正数:最多取该数量的字节;为负数:从末尾去掉对应字节数;省略则取到结尾
例如:$str = "你好World";(UTF-8下共11字节:“你好”占6字节,“World”占5字节)
substr($str, 0, 2)返回前2个字节——很可能只是“你”字的前半部分,显示为乱码。
立即学习“PHP免费学习笔记(深入)”;
中文截取必须用mb_substr(),不能硬套substr()
中文在UTF-8中是多字节字符(通常3字节/字),substr()按字节切会把一个汉字生生劈开,结果不可预测。
- 正确做法:改用
mb_substr($str, $start, $length, 'UTF-8') - 它按“字符数”而非“字节数”计算,能完整保留每个汉字、标点甚至Emoji
- 务必显式传入编码,如
'UTF-8';依赖内部编码易出错
对比示例:
$str = "Hello你好?";
-
substr($str, 0, 5)→ 可能输出"Hello"(幸运)或"Hell"(若第5字节落在“你”中间) -
mb_substr($str, 0, 5, 'UTF-8')→ 稳定输出"Hello"(5个字符) -
mb_substr($str, 5, 2, 'UTF-8')→ 输出"你好"(跳过5个ASCII字符后取2个汉字)
常见坑点与规避建议
很多线上Bug都源于这几个细节:
-
没检测原始编码:用
mb_detect_encoding($str, ['UTF-8', 'GBK'], true)粗略判断,但更推荐项目统一UTF-8并全程强制声明 - 混淆length单位:substr的$length是字节数,mb_substr的是字符数,混用必错
-
忽略空字符串或超长偏移:当
$start超过字符串长度时,substr返回空串(PHP 8+),此前版本可能返回false,需判空 - Emoji等4字节字符要特别注意:某些旧版环境对4字节UTF-8支持不全,建议测试含?、?等字符的场景
什么时候还能放心用substr()?
并非完全弃用,它在以下场景高效又安全:
- 处理纯ASCII内容(如token、base64、URL路径、日志ID等)
- 做字节级操作,比如截取二进制协议头、提取固定偏移的校验字段
- 配合
strlen()做长度判断(因两者都基于字节,逻辑自洽)
只要明确对象是单字节安全的,substr()仍是轻量首选。但只要涉及用户可见文本,尤其是含中文、繁体、日文或表情,立刻切换到mb_substr()。



















