strlen() 返回字节数而非字符数,UTF-8文本中易误判长度且引发内存溢出;应依需求选fstat()估算、mb_strlen()精确计数或流式分块处理。

直接用 strlen() 会崩?先看字符编码
PHP 的 strlen() 返回的是字节数,不是“字符数”。对 UTF-8 编码的大文本(比如含中文、emoji 的日志或用户输入),strlen() 可能远大于实际可读字符长度,且在内存吃紧时容易触发 Fatal error: Allowed memory size exhausted —— 尤其当你把整个大文件 file_get_contents() 进来再测长时。
真正要测“长度”,得先明确需求:
– 是判断是否超限(如限制 10 万字符)?
– 还是必须精确返回 Unicode 字符个数?
– 文本来源是文件流、HTTP body 还是数据库字段?
- 若只是粗略判断是否“过大”,别加载全文,用
fstat()查文件大小更轻量 - 若需 UTF-8 字符计数,
mb_strlen($str, 'UTF-8')是标准解,但前提是字符串已载入内存 - 对 >10MB 的字符串,
mb_strlen()本身也会消耗可观内存和时间,不推荐无条件使用
大文件不加载进内存怎么估长度?
多数场景下,“大文本长度”本质是风控或限流需求(如评论不能超 5000 字)。这时根本不需要精确字符数,查文件体积 + 合理系数即可估算:
if ($fp = fopen('/path/to/big.txt', 'rb')) {
$size = fstat($fp)['size'];
fclose($fp);
// UTF-8 中文平均 3 字节/字符,英文 1 字节,取保守系数 2.5
$approx_chars = (int) ceil($size / 2.5);
if ($approx_chars > 5000) {
throw new Exception('Text likely exceeds limit');
}
}
- 系数 2.5 适合中英混排;纯英文文本可用 1.1,纯中文可用 2.8–3.0
-
fstat()不读内容,毫秒级完成,无内存压力 - 注意:该法无法处理 BOM、换行符归一化等逻辑,仅作前置快速拦截
真要精确算 UTF-8 字符数?分块 + mb_substr() 避内存炸
当业务强依赖精确字符数(如计费按字收费、合规截断),又无法避免大文本,就得绕过全量加载。核心思路:用 fopen() 流式读取,每次读固定字节数,用 mb_strlen() 累加,但必须处理跨字符截断问题 —— UTF-8 多字节字符不能被切开。
简单可靠的做法是:用 mb_substr($chunk, 0, -1, 'UTF-8') 自动剥离不完整字符,再计数:
立即学习“PHP免费学习笔记(深入)”;
$fp = fopen('/path/to/huge.txt', 'rb');
$char_count = 0;
$buffer_size = 8192; // 每次读 8KB
while (!feof($fp)) {
$chunk = fread($fp, $buffer_size);
if ($chunk === false) break;
// 剥离可能的截断字符(末尾非完整 UTF-8 序列)
$safe_chunk = mb_substr($chunk, 0, mb_strlen($chunk, 'UTF-8'), 'UTF-8');
$char_count += mb_strlen($safe_chunk, 'UTF-8');
}
fclose($fp);
-
mb_substr($chunk, 0, mb_strlen($chunk, 'UTF-8'), 'UTF-8')这步看似冗余,实为保险:确保传给mb_strlen()的是合法 UTF-8 子串 - 实际测试中,
$buffer_size设为 4096–16384 平衡 I/O 和精度;太小导致调用过频,太大增加单次内存峰值 - 该法比全量
file_get_contents()+mb_strlen()内存占用低 90%+,适合百 MB 级文本
mb_strlen() 报错 “No such file or directory”?检查扩展和编码声明
这个错误不是路径问题,而是 PHP 找不到 mbstring 扩展,或未显式指定编码。常见于 Docker 镜像、Alpine 环境或精简版 PHP 安装。
- 运行
php -m | grep mbstring确认扩展已启用;没输出就需docker-php-ext-install mbstring或修改php.ini -
mb_strlen($str)在未设默认编码时行为不可靠,务必写全:mb_strlen($str, 'UTF-8') - 若文本来源不可控(如上传的 CSV、旧系统导出),先用
mb_detect_encoding($str, ['UTF-8', 'GB2312', 'ISO-8859-1'], true)探测,再转码
真正难的不是“怎么算”,而是想清楚:你到底需要字节数、图形符号数,还是语义上的“字”——三者在中文场景里可以差 3 倍。别让 strlen() 的快捷,掩盖了需求模糊的问题。











