substr_count() 是 php 中统计子串出现次数最直接、安全的方法,不依赖正则、不修改原字符串、支持多字节编码(需保证输入编码一致),但不支持重叠匹配。

用 substr_count() 最直接
统计某个子串(包括单字符)在字符串中出现的次数,substr_count() 是 PHP 原生最匹配、最安全的选择。它不依赖正则,不改变原字符串,也不受编码影响(前提是传入的字符串和搜索内容编码一致)。
常见错误是误用 count_chars() 或手动循环 —— 前者只适合统计所有字符频次,后者容易漏掉重叠匹配(比如统计 "aa" 在 "aaa" 中出现次数,实际应为 2,但朴素循环常得 1)。
-
substr_count($str, $needle):基础用法,区分大小写 - 第三个参数可指定起始位置,第四个参数指定最大搜索长度,适合处理大字符串分段统计
- 不支持重叠匹配(这是设计行为,不是 bug),如需重叠统计(如
"aba"在"ababa"中出现 2 次),得自己写循环 +strpos()
echo substr_count("hello world", "l"); // 输出 3
echo substr_count("abababa", "aba"); // 输出 2(非重叠)
区分大小写?看需求选函数或预处理
substr_count() 默认严格区分大小写。如果要忽略大小写统计,PHP 没有内置的 ignore-case 版本,不能直接传标志位 —— 常见错误是试图加 STR_CASE_LOWER 这类常量,但该函数根本不接受。
正确做法是统一转码后统计,推荐用 mb_strtolower()(多字节安全)或 strtolower()(仅 ASCII 场景):
- 中文、日文等 Unicode 字符必须用
mb_strtolower($str, 'UTF-8'),否则strtolower()可能出乱码或报错 - 若已确认字符串全是 ASCII(如纯英文+数字),
strtolower()更轻量 - 别用
mb_substr_count()—— 这个函数根本不存在,是常见拼写幻觉
$str = "Hello World HELLO"; echo substr_count(mb_strtolower($str), "hello"); // 输出 2
统计 Unicode 字符(如 emoji、中文)要小心编码
PHP 的 substr_count() 按字节操作,对 UTF-8 编码的多字节字符(比如 ?、你好)本身没问题 —— 只要 $needle 和 $str 都是合法 UTF-8 字符串,它就能正确计数。问题常出在来源混乱:
- 从数据库读取时未设好连接编码(如 MySQL 连接没执行
SET NAMES utf8mb4),导致字符串内部已损坏 - 文件保存为 GBK 却当 UTF-8 读入,
substr_count()会把半个汉字当两个无效字节处理,结果不可信 - 用
strlen()判断长度再切片统计?别这么做 —— 它返回字节数,不是字符数;应改用mb_strlen($str, 'UTF-8')
验证是否 UTF-8 合法:可用 mb_check_encoding($str, 'UTF-8') 先判断,避免静默错误。
性能敏感场景:避免重复计算和大字符串全量扫描
如果要在循环里高频调用 substr_count() 统计同一字符串中多个不同字符,每次调用都会重新遍历整个字符串 —— 这很浪费。此时更适合一次性用 count_chars($str, 1) 获取所有字节频次(仅限单字节,即 ASCII),或用 mb_substr() + 关联数组手动拆解 Unicode 字符再统计。
-
count_chars()返回的是字节值(0–255)对应出现次数的数组,不适用于中文/emoji - 对 Unicode 字符串,先用
preg_match_all('//u', $str, $matches)提取所有字符,再用array_count_values($matches[0]),但正则有开销,仅当字符种类少、字符串不大时划算 - 真正超大文本(如百 MB 日志)建议用流式处理 + 自定义状态机,而不是载入内存后统计
多数业务场景下,老老实实用 substr_count() 就够了;过度优化反而引入编码和边界问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











