strlen() 按字节计数,零宽字符(如 U+200B)在 UTF-8 中占 3 字节,故返回 3,但语义长度应为 0;应改用 mb_strlen($str, 'UTF-8') 并配合正则清除零宽字符后校验。

PHP 中 strlen() 为什么算不准含零宽字符的字符串长度
strlen() 按字节计数,而零宽字符(如 \u200b、\u200c、\u200d、\ufeff)在 UTF-8 编码下占多个字节(通常是 3 字节),但语义上“不可见、不占位”。所以一个看似空的字符串 "\xe2\x80\x8b"(U+200B 的 UTF-8 编码)用 strlen() 返回 3,而人眼和业务逻辑通常认为它“长度为 0”或“应被忽略”。
真正需要的是按 Unicode 码点(grapheme)或视觉宽度判断,而非原始字节。
用 mb_strlen() 替代 strlen() 时要注意编码参数
mb_strlen() 默认使用 mb_internal_encoding(),若未显式设为 'UTF-8',可能因环境差异返回错误结果。尤其在 CLI 模式或某些旧 PHP 配置下,内部编码可能是 ISO-8859-1,导致零宽字符被截断或误判。
- 务必显式传入
'UTF-8'第二个参数:mb_strlen($str, 'UTF-8') - 避免依赖
mb_internal_encoding()的全局设置,它容易被其他库修改 - 注意:即使用了
mb_strlen(),U+200B、U+200C 等仍会计为 1 个码点——这符合 Unicode 标准,但业务上你可能想过滤掉它们
检测并剥离常见零宽字符的实用正则写法
零宽字符不是“空白”,trim() 和 ctype_space() 完全无效。必须用 Unicode 属性或具体码点匹配。
立即学习“PHP免费学习笔记(深入)”;
- 最简方案(覆盖主流零宽控制符):
preg_replace('/[\x{200b}-\x{200f}\x{202a}-\x{202e}\x{feff}]/u', '', $str) - 更严格(仅零宽连接/分隔类):
preg_replace('/[\x{200b}\x{200c}\x{200d}\x{2060}\x{feff}]/u', '', $str) - 替换后建议再用
mb_strlen($cleaned, 'UTF-8')判断有效长度 - 注意
/u修饰符必不可少,否则 UTF-8 多字节序列会被当乱码处理
用户输入场景下建议的完整校验流程
比如表单昵称、评论内容等需防零宽注入的字段,不能只靠长度判断,得组合清洗与语义检查。
- 先用
mb_convert_encoding($input, 'UTF-8', 'UTF-8')归一化编码(修复损坏的 UTF-8 序列) - 用上述正则清除零宽控制符
- 用
mb_ereg_replace('^\\s+|\\s+$', '', $cleaned)或trim()去首尾空白(注意:零宽字符不在\s范围内) - 最后用
mb_strlen($final, 'UTF-8') === 0判空,或>= 2判最小有效长度 - 若需保留排版类零宽(如某些阿拉伯语连字控制符),则要白名单过滤,而非通杀
零宽字符本身合法且必要,问题出在滥用;关键不是“删光”,而是“识别意图”——用户真想提交一个纯零宽字符串?那大概率是测试或攻击。











