最直接方法是用 preg_replace('/[\p{P}\p{S}]+/u', '', $str) 去标点后调用 mb_strlen($cleaned_str, 'UTF-8'),必须显式指定 UTF-8 编码,否则中文长度计算错误。

用 preg_replace 去掉标点再测长度最直接
PHP 没有内置“忽略标点的字符串长度”函数,得自己剥离标点再用 strlen 或 mb_strlen。核心思路是:先用正则把 Unicode 标点字符(包括中文顿号、句号、英文逗号等)全部替换为空,再计算剩余字符数。
常见错误是只过滤 ASCII 标点,比如写成 /[^\w\s]/,结果中文标点(如《》、【】、,。!?)全被算进去了。
- 推荐正则:
/[\p{P}\p{S}]+/u——\p{P}匹配所有 Unicode 标点,\p{S}匹配符号(如 ©、★、→),u修饰符启用 UTF-8 模式 - 安全写法:
mb_strlen(preg_replace('/[\p{P}\p{S}]+/u', '', $str), 'UTF-8') - 如果只要字母数字和汉字,可收紧为:
/[^\p{L}\p{N}\s]+/u(\p{L}是字母,\p{N}是数字)
mb_strlen 必须指定编码,否则中文会算错
不加编码参数的 mb_strlen($str) 依赖 mb_internal_encoding() 当前设置,线上环境常为 ISO-8859-1,导致中文字符全被当单字节处理,长度直接腰斩。
例如:$str = "你好,world!";,未指定编码时可能返回 12(错误),正确应为 7(“你好world”共 7 个非标点字符)。
立即学习“PHP免费学习笔记(深入)”;
- 始终显式传入
'UTF-8':mb_strlen($cleaned_str, 'UTF-8') - 别依赖
setlocale或mb_internal_encoding全局设置,不同模块可能互相覆盖 - 测试时可用
mb_detect_encoding($str)看原始编码,但不建议用于生产逻辑判断
性能敏感场景慎用 preg_replace 多次调用
如果要对大量字符串反复做“去标点测长”,每次调用 preg_replace 会有编译正则的开销。PHP 7.4+ 对重复正则有缓存,但低版本或复杂正则仍可能拖慢。
- 高频场景可预编译正则:
preg_replace('~[\p{P}\p{S}]+~u', '', $str)中的波浪线定界符比斜杠更少转义麻烦 - 极端性能要求下,可改用
mb_substr+ 字符逐个判断,但代码膨胀且易漏判;不如升级 PHP 版本或加 Redis 缓存结果 - 注意:不要用
str_replace列出所有标点手工替换——中英文标点组合超百种,维护成本爆炸
注意全角空格、零宽字符这些“隐形标点”
用户粘贴内容常含全角空格( )、零宽空格(\xe2\x80\x8b)、软连字符(\xc2\xad)等,它们不属于 \p{P},但也不该计入有效长度。
这类字符在浏览器里看不见,却会让 mb_strlen 多算,甚至引发数据库字段超长报错。
- 补充清理:
preg_replace('/[\x{2000}-\x{206F}\x{2028}\x{2029}\x{FEFF}]+/u', '', $str)(覆盖常用空白与控制符) - 更彻底方案:用
Normalizer::normalize($str, Normalizer::FORM_C)先标准化,再过滤 - 调试技巧:用
bin2hex($str)查看原始字节,比肉眼排查靠谱得多











