substr_count()仅支持字面连续匹配,适合固定子串统计;count_chars()专用于ascii字符频次;utf-8需用mb_substr()循环+关联数组,且须设mb_internal_encoding("utf-8")并用mb_strlen()。

用 substr_count() 统计子串出现次数最直接
它只管“连续匹配”,适合统计固定字符串、关键词或单个字符(比如统计空格、换行符)。注意:不区分大小写,也不支持正则,纯字面匹配。
常见错误是误以为它能统计「字符频率」——比如想算 "a" 在 "abac" 里出现几次,用它没问题;但想一次性得出所有字符的频次表,它就不顶用了。
- 对单字符统计:可用,但效率不如
count_chars() - 对多字符子串(如
"the"):唯一推荐方式 - 参数顺序是
substr_count($haystack, $needle),别把字符串和子串位置写反 - 不支持重叠匹配:比如
substr_count("aaaa", "aa")返回2,不是3
count_chars() 是统计单字节字符频率的正确工具
它专为字符频次设计,返回数组索引是 ASCII 码值,值是出现次数。适用于 ASCII 字符集为主的场景(英文、数字、标点),但对 UTF-8 多字节字符(如中文、emoji)会按字节拆开,结果不可信。
典型错误是拿它处理中文字符串还期待得到“汉字”频次——实际得到的是每个 UTF-8 字节的计数,完全失真。
立即学习“PHP免费学习笔记(深入)”;
- 要获取频次表:传入第二个参数为
1,例如count_chars($str, 1) - 要只看出现过的字符:用
2,返回 ASCII 码数组 - 要转成可读字符:得手动
chr($ascii),且仅限0–255范围 - UTF-8 字符串必须先转成单字节编码(不推荐)或换方案
处理 UTF-8 字符串必须用 mb_substr() 循环 + 关联数组
PHP 原生没有内置的 UTF-8 字符频次函数,count_chars() 和 str_split() 都会破坏多字节结构。唯一稳妥方式是逐个取出 Unicode 字符,再累加计数。
容易忽略的是:没检查 mb_internal_encoding() 是否设为 "UTF-8",导致 mb_substr() 行为异常;还有人用 strlen() 当循环上限,结果遍历的是字节数而非字符数。
- 先确认:
mb_internal_encoding("UTF-8") - 用
mb_strlen($str)获取字符总数,而非strlen($str) - 循环中用
mb_substr($str, $i, 1)取第$i个字符 - 用关联数组累加:
$freq[$char] = ($freq[$char] ?? 0) + 1
性能差异明显:小文本无所谓,大文本得选对方法
substr_count() 是 C 层实现,速度最快;count_chars() 也是底层优化过的,几 MB 以内 ASCII 文本都很快;但 UTF-8 循环方案是纯 PHP 实现,每取一个字符都要调用 mb_substr(),开销大。
线上处理日志或用户输入时,如果确定不含中文,就别硬上 mb 方案——既慢又容易出错。
- 纯英文/数字文本:优先
count_chars($str, 1) - 含中文、日文等:必须用
mb_substr()循环,别图省事用str_split() - 超长字符串(>10MB):考虑流式分块或改用扩展(如
mbstring的其他函数) - 注意
mb_substr()第三个参数默认是字节数,必须显式传1表示“1 个字符”











