不能用for循环安全遍历UTF-8字符串测长,因PHP字节索引会将1个汉字误拆为3个乱码字节;正确做法是用mb_substr配合while循环逐字符截取并计数。

为什么不能用 for 循环安全遍历 UTF-8 字符串测长
PHP 的 for 循环配合 $str[$i] 或 substr($str, $i, 1) 遍历字符串,本质是按字节索引操作。UTF-8 中一个中文字符占 3 字节,$str[0] 只取第一个字节,结果是乱码片段,且计数会把 1 个汉字算成 3 个“字符”。这不是长度不准的问题,而是根本性误读编码。
手动遍历测长的唯一可行方式:用 mb_substr + while
必须脱离字节索引,改用多字节安全函数逐字符截取。核心是用 mb_substr($str, $i, 1, 'UTF-8') 判断是否还能取出完整字符:
-
$i从 0 开始,每次成功截取后$i++ - 当
mb_substr($str, $i, 1, 'UTF-8')返回空字符串(不是false),说明已到末尾 - 注意:
mb_internal_encoding('UTF-8')最好显式设置,避免依赖默认编码
$len = 0;
$i = 0;
while (mb_substr($str, $i, 1, 'UTF-8') !== '') {
$len++;
$i++;
}
for 循环强行套用的典型错误写法
以下代码看似简洁,实则危险:
for ($i = 0; $i < strlen($str); $i++) {
$char = $str[$i]; // 错:$str[$i] 是字节访问
$count++;
}
它在 ASCII 字符串中能碰巧正确,但只要含中文、emoji 或任何非 ASCII 字符,$str[$i] 就可能返回不完整 UTF-8 序列(如 \xe4 单独出现),mb_strlen() 和这个循环的结果必然不一致。别依赖 strlen() 做上限判断——那是字节数,不是字符数。
立即学习“PHP免费学习笔记(深入)”;
真正需要手动遍历的场景其实极少
除非你在写字符级解析器(比如手写词法分析、逐字高亮、自定义断行逻辑),否则直接用 mb_strlen($str, 'UTF-8')。它底层已优化,比 PHP 层 while 循环快一个数量级。手动遍历只在两种情况必要:
- 需在遍历中对每个字符做复杂判断(如跳过某类 Unicode 类别)
- 环境受限无法启用 mbstring 扩展(此时应优先修复环境,而非硬写循环)
多数人想“手动测长”,其实是没意识到 mb_strlen 就是标准解法——而 for 循环遍历字节,从来就不是测“字符串长度”的合理手段。











