strlen返回错误长度是因为它按字节而非字符计数,utf-8中一个汉字占3字节;应统一使用显式指定编码的mb_strlen、mb_substr等函数,并确保数据库、文件、连接层全链路utf-8。

为什么 strlen 在中文里总是返回错误长度
因为 strlen 只数字节,不识字符。UTF-8 中一个汉字占 3 字节,strlen('你好') 返回 6,但你实际要的是“2 个字符”。这会导致截断错位、分页计算偏差、表单校验失败等——尤其在用户昵称、地址、标题这类多语言字段上特别明显。
解决办法不是硬算除以 3,而是换用真正按字符计数的函数:
-
mb_strlen($str, 'UTF-8')替代strlen($str) - 必须显式传入编码,不能依赖
mb_internal_encoding()的全局设置(它可能被其他模块改掉) - 如果字符串编码不确定,先用
mb_detect_encoding($str, ['UTF-8', 'GB2312', 'BIG5'], true)探测,再传给mb_*函数
用 mb_substr 截取中文时为什么还是乱码
常见错误是混用 substr 和 mb_substr:比如先用 substr($str, 0, 10) 截出前 10 字节,再传给 mb_strlen——这已经把 UTF-8 字符切成了半截,后续任何 mb_* 都救不回来。
正确做法是全程使用 mb_* 系列,且起始偏移和长度单位统一为“字符”:
-
mb_substr($str, 0, 5, 'UTF-8')→ 取前 5 个字符,不是 5 字节 - 第三个参数(长度)为
null表示到末尾,mb_substr($str, 10, null, 'UTF-8') - 避免用
mb_strcut,它按字节截但保持字节完整,结果仍是字节长度,不是语义字符
数据库存取时 mb_* 函数还管用吗
不管用——mb_* 只处理 PHP 内存中的字符串,不解决 MySQL 或 PostgreSQL 的编码链路问题。即使你用 mb_strlen 算对了长度,如果数据库连接、表字段、甚至 PDO 的 PDO::MYSQL_ATTR_INIT_COMMAND 没设成 SET NAMES utf8mb4,存进去的仍是乱码或截断。
关键检查点:
- PHP 连接数据库时显式设置字符集:
$pdo->exec("SET NAMES utf8mb4") - MySQL 表字段用
utf8mb4_unicode_ci,不是utf8_general_ci(后者不支持 emoji) - PHP 文件本身保存为 UTF-8 无 BOM,否则
mb_detect_encoding可能误判
性能影响大不大?能不能只在必要处用
有影响,但通常可忽略。实测 10 万次 mb_strlen 比 strlen 慢约 3–5 倍,但在 Web 请求中占比微乎其微。真成瓶颈的从来不是这个,而是没做缓存、反复查库、或用 mb_convert_encoding 在循环里转码。
建议策略:
- 所有用户输入、API 返回、模板渲染涉及字符串长度/截取/比较的地方,无条件用
mb_* - 内部纯 ASCII 标识符(如 UUID、base64 token)可继续用
strlen,但需确保来源可控 - 别为了“省一点性能”在关键路径上降级,中文截断错一个字,前端显示就是 符号,比慢几微秒更难排查
最常被忽略的一点:mb_* 函数默认编码是 mb_internal_encoding() 的返回值,而这个值在 CLI 和 FPM 下可能不同,线上环境容易因此出现本地测试正常、部署后出错的情况——所以每个调用都显式传 'UTF-8' 最稳妥。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











