scandir 默认返回 . 和 .. 是 POSIX 兼容行为,并非 bug;需用 array_diff($files, ['.', '..']) 过滤,避免误删隐藏文件;排序按字节序,可用 natsort() 或 usort() 配合 filemtime() 调整;中文乱码源于编码不匹配,应统一环境为 UTF-8;大目录易内存溢出,建议改用 opendir/readdir 流式处理。

scandir 读取目录时为什么漏掉 . 和 ..?
scandir 默认会返回 .(当前目录)和 ..(父目录),这不是 bug,是 POSIX 兼容行为。很多新手以为“没读到文件”其实是被这两个条目干扰了逻辑判断。
常见错误现象:foreach 遍历时直接对 scandir 结果做 is_file() 判断,结果 . 和 .. 触发警告或跳过处理,误以为目录为空。
- 用
array_diff($files, ['.', '..'])过滤掉这两项最稳妥 - 别用
substr($f, 0, 1) === '.'粗暴过滤,会误删真实以点开头的合法文件(比如.env) - 如果只要文件不要目录,得配合
is_file()或is_dir()单独判断,不能只靠文件名特征
scandir 排序乱、顺序不可控怎么办?
scandir 返回数组默认按字节序(ASCII order)升序排列,不是自然排序,也不是修改时间顺序。比如 file10.txt 会排在 file2.txt 前面。
使用场景:需要按名称逻辑排序(如版本号、编号)、或按时间倒序列出最新文件时,必须手动干预。
立即学习“PHP免费学习笔记(深入)”;
- 按字母自然排序用
natsort(),注意它不改变键名,要用array_values()重索引 - 按修改时间倒序:先
scandir,再usort()+filemtime(),但注意路径拼接别出错($path . '/' . $f) - Windows 下大小写不敏感,Linux 敏感,排序结果可能跨平台不一致,别假设顺序稳定
目录里有中文名或特殊字符,scandir 返回乱码或空数组?
PHP 本身不处理文件系统编码,scandir 返回的是原始字节流。在 UTF-8 环境下读取 GBK 编码的目录(常见于旧 Windows),就会出现乱码或跳过条目。
错误现象:scandir('/path/中文') 返回空数组,或文件名变成 .txt;iconv() 转换后又报 “illegal character”。
- 先确认当前 PHP 进程的 locale(
setlocale(LC_CTYPE, 0)),再匹配目录实际编码 - 避免在代码里硬转编码,优先统一环境:Web 服务器、终端、文件系统都设为 UTF-8
- 实在要兼容,用
mb_convert_encoding($name, 'UTF-8', 'GBK'),但得兜底mb_detect_encoding()失败的情况
大目录下 scandir 卡死或内存爆掉?
scandir 是一次性把所有文件名加载进内存的,遇到几万文件的目录,很容易触发 memory_limit 或超时。不是函数慢,是设计如此。
性能影响:10 万个文件大概吃掉 50–100MB 内存,且 scandir 本身调用开销随文件数线性增长。
- 换成
opendir()+readdir()流式读取,边读边处理,内存恒定 - 加
clearstatcache()防止反复stat拖慢速度(尤其配合is_file()时) - 真要分页读大目录,别用
array_slice,改用readdir循环跳过前 N 项
复杂点在于:scandir 看似简单,但一旦涉及编码、排序、大目录、隐藏文件,每个环节都得单独补漏。没人会在意这些细节,直到线上出问题。











