根本原因是length统计字节数而非字符数:utf-8中英文1字节/字符、中文3字节/字符、emoji可达4字节,导致strlen("你好")=6但字符数仅为2,引发校验、截断、分页等逻辑错误。

直接用字符串的 length(或类似 strlen())值判断字符数量,在双字节(或多字节)字符环境下会失效,根本原因在于:这个“长度”统计的是字节数,不是字符数。
字节长度 ≠ 字符长度
在 UTF-8 编码中:
- 英文、数字、ASCII 符号:1 个字符 = 1 字节
- 中文、日文、韩文等常用汉字:1 个字符 = 3 字节
- 部分生僻字或 Emoji:可能占 4 字节
例如:"你好" 实际是 2 个字符,但 strlen("你好") 返回 6;"a你" 是 2 个字符,strlen 却返回 4。若业务逻辑按“长度 ≥ 8”限制密码,用户输 8 个中文,strlen 得到 24,误判为超长;输 3 个中文 + 1 个字母,strlen 得 10,又误判达标——验证完全失准。
前端与后端不一致加剧问题
JavaScript 的 String.prototype.length 在多数现代引擎中返回的是 UTF-16 码元数:
- 普通字符(如
'A'、'中'):1 个码元 →.length === 1 - 增补平面字符(如某些 Emoji):占 2 个码元 →
.length === 2,但实际是 1 个字符
这就导致:前端用 .length 校验通过的字符串,后端用 strlen() 检查时结果不同;反之亦然。表单提交后端校验失败,用户体验割裂。
截断、分页、显示逻辑全部错位
依赖字节长度做 UI 控制时,后果直观可见:
- 评论截取前 20 字:用
substr($str, 0, 20)可能只拿到 6 个中文,还把第 7 个字截成乱码(UTF-8 中途断开) - 分页按“每页 100 字符”计算:字节长度达 300 才翻页,实际只显示了约 33 个汉字
- 输入框限制
maxlength="10":HTML 层面限制的是 UTF-16 码元数,用户粘贴一个 4 字节 Emoji 就占掉 2 个位置
正确做法:统一使用字符语义长度
关键不是“不用 length”,而是用对的 length:
- PHP:一律用
mb_strlen($str, 'UTF-8'),并确保mbstring扩展启用、内部编码设为 UTF-8 - JavaScript:对严格字符计数,用
[...str].length或Array.from(str).length(兼容 Unicode);避免str.length直接用于多语言场景 - Java:用
String.codePointCount(0, s.length())替代s.length()处理增补字符 - 数据库层:字段定义用
VARCHAR(32)是指 32 个字符(MySQL 5.5.3+ UTF-8mb4 下),但 ORDER BY / GROUP BY 仍需注意 collation 对多字节排序的影响











