应使用 array_filter() 配合 pathinfo($f, PATHINFO_EXTENSION) 筛选指定后缀文件,因其能正确处理路径、大小写及多点文件名;避免用 end(explode()) 或 substr() 等易出错的字符串截取方式。

用 array_filter() + pathinfo() 筛选带指定后缀的文件名
PHP 数组里存了一堆文件名(比如 ['report.pdf', 'data.csv', 'notes.txt', 'image.jpg']),想只留下 .csv 和 .txt 的?直接用 array_filter() 配合 pathinfo() 最稳妥——它专为解析路径/文件名设计,不会被点号在文件名中间(如 my.file.txt)误伤。
关键点:别用 substr() 或 strrchr() 硬截取,它们不区分扩展名和路径分隔符或多余点;pathinfo($file, PATHINFO_EXTENSION) 才是语义正确的解法。
- 示例代码:
$files = ['report.pdf', 'data.csv', 'notes.txt', 'image.jpg']; $csvAndTxt = array_filter($files, function($f) { return in_array(pathinfo($f, PATHINFO_EXTENSION), ['csv', 'txt']); }); - 注意
pathinfo()返回的是小写扩展名(即使原文件是DATA.CSV,也会返回csv),所以比较时统一用小写数组 - 如果数组里混有完整路径(如
/var/log/app.log),pathinfo()依然能正确提取扩展名,无需先basename()
用 fnmatch() 做模糊后缀匹配(支持通配符)
需要匹配一组相似后缀,比如所有以 .log 结尾、但可能带日期后缀(app.log.20240501)?或者想一次性抓 .php 和 .inc?fnmatch() 更灵活,且底层调用系统 glob 规则,性能略优。
-
fnmatch('*.php', $filename)返回true表示匹配成功 - 可一次写多个模式:
$patterns = ['*.php', '*.inc', '*.html']; $phpFiles = array_filter($files, function($f) use ($patterns) { foreach ($patterns as $p) { if (fnmatch($p, $f)) return true; } return false; }); - 注意:
fnmatch()在 Windows 下对大小写不敏感,在 Linux/macOS 默认敏感;若需跨平台一致,建议统一转小写再比:fnmatch('*.php', strtolower($f))
避免 end(explode('.', $file)) 这类写法
这种写法看似简洁,实际埋了三个坑:遇到无后缀文件(README)会返回空字符串;遇到隐藏文件(.gitignore)会错误返回 gitignore;遇到多点文件名(archive.tar.gz)只取到 gz,而你真正想要的是 tar.gz。这不是 bug,是逻辑错位。
立即学习“PHP免费学习笔记(深入)”;
- 扩展名 ≠ 最后一个点之后的内容,而是「最后一个点之后、且不包含路径分隔符的部分」
-
pathinfo()内部已处理这些边界,end(explode())属于用字符串操作模拟路径逻辑,不可靠 - 若真要手动拆,至少用
strrpos($file, '.')判断是否存在点,再结合basename()安全截取
性能与内存:大数据量时优先用 foreach 替代 array_filter()
当数组含上万条文件名,且筛选条件简单(如只留 .jpg),foreach 显式循环比匿名函数 + array_filter() 略快 5–10%,内存占用也更可控——因为避免了闭包创建和回调栈开销。
- 等效写法:
$jpgFiles = []; foreach ($files as $f) { if (pathinfo($f, PATHINFO_EXTENSION) === 'jpg') { $jpgFiles[] = $f; } } - 尤其在 CLI 脚本或定时任务中,这点差异会累积;但日常几百条,完全不用纠结
- 如果后续还要链式操作(如排序、去重),再考虑保持函数式风格











