应使用 mb_strlen($str, 'UTF-8') 计算中文字符串长度,因 strlen 按字节计算导致 UTF-8 中文被误计为 3 字符/字;需确保 mbstring 扩展启用并显式指定编码。

PHP里用 strlen 算中文字符串长度会错,因为它是按字节算的
中文字符在 UTF-8 下占 3 字节,strlen 把一个汉字当 3 个“字符”返回。比如 "你好",strlen 返回 6,但你实际想问的是“几个字”,答案是 2。
这不是 bug,是设计使然:strlen 本意就是返回字节数,和编码无关。它快、轻量、C 层实现,适合判断二进制数据长度或 ASCII 场景。
- 只处理纯英文、数字、ASCII 符号时,
strlen完全够用 - 只要字符串含中文、日文、emoji 或任意非 ASCII 字符(尤其 UTF-8 编码),就别信
strlen的结果 - 框架或用户输入场景下,几乎必然踩坑——表单校验限制 10 个字,结果用户输 4 个汉字就被截断,后台还报“超出长度”
该用 mb_strlen 时,必须显式指定编码参数
mb_strlen 是多字节安全的,但它不会自动猜你用的是 UTF-8 还是 GBK。不传第二个参数,它依赖 mb_internal_encoding() 的当前值,而这个值可能被其他代码改过,也可能没设,默认是 ISO-8859-1——这时连英文都算不对。
- 永远写成
mb_strlen($str, 'UTF-8'),别省略第二个参数 - 不要依赖
ini_set('mbstring.internal_encoding', 'UTF-8'),PHP 8.2+ 已废弃该 ini 配置 - 如果读的是数据库字段,且字段是
utf8mb4,那编码一定是 UTF-8;如果是旧系统用gbk,就得写'GBK' -
mb_strlen比strlen慢一点,但日常 Web 请求中差异可忽略,别为这点性能牺牲正确性
遇到 mb_strlen 返回 0 或奇怪数字?先查字符串是否为空或含 BOM
常见错误现象:明明写了 "测试",mb_strlen($s, 'UTF-8') 却返回 0;或者返回比预期多 1。
立即学习“PHP免费学习笔记(深入)”;
- 检查是否误传了
null或false——mb_strlen(null, 'UTF-8')返回 0,不报错也不警告 - 文件保存为 UTF-8 with BOM 时,开头三个字节
\xEF\xBB\xBF会被当成字符,mb_strlen在 UTF-8 下能识别 BOM,但会把它算作 1 个字符(其实是不可见的) - 用
bin2hex(substr($s, 0, 3)) === 'efbbbf'快速检测 BOM - 从
$_POST或file_get_contents读来的字符串,可能带换行、空格、\0 结束符,用trim()或rtrim($s, "\0")清理后再测
兼容性注意:mbstring 扩展不是默认开启的
很多 Docker 镜像、Alpine PHP、或最小化安装的环境里,mbstring 是关着的。直接调用 mb_strlen 会报 Fatal error: Uncaught Error: Call to undefined function mb_strlen()。
- 上线前务必检查:
php -m | grep mbstring或extension_loaded('mbstring') - 不能假设函数存在——PHP 7.4+ 默认启用,但老版本或定制编译仍可能缺失
- 如果真没法开扩展,又必须处理中文长度,可用
preg_match_all('/./u', $str)替代(慢且有边界 case 问题),但不如直接加扩展靠谱 - CI/CD 流程里,在 phpunit 前加一步
assert extension_loaded('mbstring'),早暴露问题
真正麻烦的不是选 strlen 还是 mb_strlen,而是同一个项目里混着用——有的地方用 strlen 校验,有的用 mb_strlen 截取,最后字符串被切在中文中间,变成乱码。统一约定、加类型提示、关键路径加断言,比记住规则更重要。











