RecursiveDirectoryIterator 类

(PHP 5, PHP 7, PHP 8)

介绍

RecursiveDirectoryIterator 提供了一个接口,用于递归迭代文件系统目录。

类概要

class RecursiveDirectoryIterator extends FilesystemIterator implements RecursiveIterator {
/* 继承的常量 */
/* 方法 */
public __construct(string $directory, int $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO)
public getSubPath(): string
public hasChildren(bool $allowLinks = false): bool
public key(): string
public next(): void
public rewind(): void
/* 继承的方法 */
public SplFileInfo::getBasename(string $suffix = ""): string
public SplFileInfo::openFile(string $mode = "r", bool $useIncludePath = false, ?resource $context = null): SplFileObject
public SplFileInfo::setFileClass(string $class = SplFileObject::class): void
public SplFileInfo::setInfoClass(string $class = SplFileInfo::class): void
}

目录

添加注释

用户贡献的注释 17 个注释

Thriault
14 年前
如果你想获取,例如,项目文件夹中所有 *.php 文件,递归地,你可以使用以下代码

<?php

$Directory
= new RecursiveDirectoryIterator('path/to/project/');
$Iterator = new RecursiveIteratorIterator($Directory);
$Regex = new RegexIterator($Iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);

?>

$Regex 将包含每个 PHP 文件的单索引数组。
sun
10 年前
由于我一直在网络上遇到无意中陷入此陷阱的实现,请注意

RecursiveDirectoryIterator 递归进入整个文件系统树,没有限制。

除非你故意要无限递归而没有限制,否则不要执行以下操作

<?php
$directory
= new \RecursiveDirectoryIterator($path);
$iterator = new \RecursiveIteratorIterator($directory);
$files = array();
foreach (
$iterator as $info) {
if (...
自定义条件...) {
$files[] = $info->getPathname();
}
}
?>

1. RecursiveDirectoryIterator 只是一个递归迭代器,它递归进入其子项,直到没有更多子项。

2. RecursiveIteratorIterator 的实例化会导致 RecursiveDirectoryIterator *立即* 无限递归进入整个文件系统树(从给定的基本路径开始)。

3. 不必要的文件系统递归很慢。在 90% 的情况下,这不是你想要的。

记住这个简单的经验法则

→ 必须对 RecursiveDirectoryIterator 进行过滤,或者你有充分的理由说明为什么不应该过滤。

在 PHP <5.4 中,实现以下内容 - 你的自定义条件将移入一个合适的过滤器

<?php
$directory
= new \RecursiveDirectoryIterator($path, \FilesystemIterator::FOLLOW_SYMLINKS);
$filter = new MyRecursiveFilterIterator($directory);
$iterator = new \RecursiveIteratorIterator($filter);
$files = array();
foreach (
$iterator as $info) {
$files[] = $info->getPathname();
}

class
MyRecursiveFilterIterator extends \RecursiveFilterIterator {

public function
accept() {
$filename = $this->current()->getFilename();
// 跳过隐藏文件和目录。
if ($name[0] === '.') {
return
FALSE;
}
if (
$this->isDir()) {
// 仅递归进入预期的子目录。
return $name === 'wanted_dirname';
}
else {
// 仅消费感兴趣的文件。
return strpos($name, 'wanted_filename') === 0;
}
}

}
?>

在 PHP 5.4 及以上版本中,PHP 核心解决了创建全新的类的繁琐问题,你可以利用新的 RecursiveCallbackFilterIterator 来代替。

<?php
$directory
= new \RecursiveDirectoryIterator($path, \FilesystemIterator::FOLLOW_SYMLINKS);
$filter = new \RecursiveCallbackFilterIterator($directory, function ($current, $key, $iterator) {
// 跳过隐藏文件和目录。
if ($current->getFilename()[0] === '.') {
return
FALSE;
}
if (
$current->isDir()) {
// 仅递归进入预期的子目录。
return $current->getFilename() === 'wanted_dirname';
}
else {
// 仅消费感兴趣的文件。
return strpos($current->getFilename(), 'wanted_filename') === 0;
}
});
$iterator = new \RecursiveIteratorIterator($filter);
$files = array();
foreach (
$iterator as $info) {
$files[] = $info->getPathname();
}
?>

玩得开心!
alvaro at demogracia dot com
15 年前
使用示例

<?php

$path
= realpath('/etc');

$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach(
$objects as $name => $object){
echo
"$name\n";
}

?>

这将打印出 $path 下的所有文件和目录列表(包括 $path 本身)。如果你想省略目录,请移除 RecursiveIteratorIterator::SELF_FIRST 部分。
TDP
7 年前
Windows 和 Linux 在文件排序方面存在差异。

<?php
$it
= new RecursiveIteratorIterator(
new
RecursiveDirectoryIterator( 'path/to/dir' )
//, 标志无关紧要
);
...
?>

在 Windows 上,你将看到按名称排序的文件。在 Linux 上,它们没有排序。
catinahat at cool dot fr dot nf
11 年前
如果你需要将嵌套的目录树转换为多维数组,请使用此代码

<?php
$ritit
= new RecursiveIteratorIterator(new RecursiveDirectoryIterator($startpath), RecursiveIteratorIterator::CHILD_FIRST);
$r = array();
foreach (
$ritit as $splFileInfo) {
$path = $splFileInfo->isDir()
? array(
$splFileInfo->getFilename() => array())
: array(
$splFileInfo->getFilename());

for (
$depth = $ritit->getDepth() - 1; $depth >= 0; $depth--) {
$path = array($ritit->getSubIterator($depth)->current()->getFilename() => $path);
}
$r = array_merge_recursive($r, $path);
}

print_r($r);
?>
antennen
13 年前
如果你使用 RecursiveDirectoryIterator 和 RecursiveIteratorIterator,并遇到 UnexpectedValueException,你可以使用这个小技巧来忽略这些目录,例如 Linux 上的 lost+found。

<?php
class IgnorantRecursiveDirectoryIterator extends RecursiveDirectoryIterator {
function
getChildren() {
try {
return new
IgnorantRecursiveDirectoryIterator($this->getPathname());
} catch(
UnexpectedValueException $e) {
return new
RecursiveArrayIterator(array());
}
}
}
?>

使用方法与正常的 RecursiveDirectoryIterator 相同。
megar
15 年前
使用示例
查看所有文件并统计空间使用情况

<?php
$ite
=new RecursiveDirectoryIterator("/path/");

$bytestotal=0;
$nbfiles=0;
foreach (new
RecursiveIteratorIterator($ite) as $filename=>$cur) {
$filesize=$cur->getSize();
$bytestotal+=$filesize;
$nbfiles++;
echo
"$filename => $filesize\n";
}

$bytestotal=number_format($bytestotal);
echo
"Total: $nbfiles files, $bytestotal bytes\n";
?>
alexandrebr at gmail dot com
8 年前
我尝试使用 RecursiveDirectoryIterator 导出一个大型目录(约 400,000 个文件)中的所有文件(以及它们的属性,如大小/是否是链接/是否是目录/修改时间/权限/所有者/组),并过滤掉一些特定需要的文件/文件夹。

使用 RecursiveDirectoryIterator 和 SplFileInfo,导出过程大约需要 50 秒才能完成,但它可以正常工作。

然而,为了提高性能,我决定制作另一个版本的相同脚本,只使用直接文件函数,如 "readdir"、"filesize"、"filemtime" 等,并自己添加递归(如果(is_dir($path))执行递归($path))。

运行之后,脚本从约 50 秒缩短到约 20 秒完成(在 Linux CentOS 7 上,SSD 300IPs)。

奇怪的是,在 Windows 7 上,Sata3(使用完全相同的文件 [镜像])的时间从约 63 秒缩短到约 57 秒。

我认为这种性能差异是由于 SPL 的面向对象方法,它运行了很多不必要的额外代码来执行相同任务,但具有更高的可靠性,而直接文件函数更像是 C 对应函数的别名,因此速度更快。

因此,如果你要处理大量文件,使用 RecursiveDirectoryIterator 可能不是最好的方法。
dev_zakaria at outlook dot com
5 年前
如果你想显示所有目录中的所有文件,请按照以下步骤操作:

$dir = new RecursiveDirectoryIterator(getcwd());
$files = new RecursiveIteratorIterator($dir);

foreach($files as $file){
echo $file->getFileName();
echo PHP_EOL; // 用于换行
}

现在,如果你想显示完整路径,请按照以下步骤操作:

$dir = new RecursiveDirectoryIterator(getcwd());
$files = new RecursiveIteratorIterator($dir);

foreach($files as $file){
echo $file->getPath().$file->getFileName();
echo PHP_EOL;
}

如果你想跳过点,你需要用以下代码替换第一行:
$dir = new RecursiveDirectoryIterator(getcwd(), RecursiveDirectoryIterator::SKIP_DOTS);
Josh Heidenreich
12 年前
返回的对象是 SplFileInfo 对象的迭代器。
flobee
9 年前
在这个文档页面中,我看到了一些隐藏隐藏文件的内容(也适用于 opendir() 或 readdir() ... 这些应该在所有地方都提到)
<?php
// 不是隐藏的,但在大多数操作系统上,如 Win、*nix、OSX ...
if ($file == '.' || $file == '..') {
// "." 当前目录信息,
// ".." 上级目录信息,
continue;
?>
or
<?php
if $name[0] === '.' // 不行,伙计们
?>
Think
"... and then came Polly.avi" 是电影的标题。你接下来该怎么办?

Windows 处理隐藏文件的方式与基于 Unix 的系统不同。

对于基于 Unix 的系统,类似这样的代码应该可以工作
<?php
if (preg_match('/^(\.\w+|\.$|\.\.$)/i', $location)) {
/* 是隐藏的:
.
..
.dir
.file
*/
}
// 应该没问题:"..some thing","... some thing"
?>

我知道你会这么做(如果 $name[0] === '.'),因为它速度更快。但它是不正确的,有一天你会像我今天一样错过一些东西 :-)
divinity76+spam at gmail dot com
2 年前
如果你只需要一个简单的递归数组,包含所有文件(而不是目录),可以尝试:

<?php

/**
* 返回一个简单的字符串数组,包含目录及其子目录中所有文件的路径,
* 不会返回目录本身。(这意味着如果一个目录是空的,它将完全不包含)
*
* @param string $dir
* @param bool $realpath
* @throws UnexpectedValueException 如果 $dir 不可读/不存在
* @return string[] files
*/
function get_file_list_recursively(string $dir, bool $realpath = false): array
{
$files = array();
$files = [];
foreach ((new
RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS))) as $file) {
/** @var SplFileInfo $file */
if ($realpath) {
$files[] = $file->getRealPath();
} else {
$files[] = $file->getPathname();
}
}
return
$files;
}
?>

示例返回值
<?php
array (
0 => '/home/hans/scraping/1650518081RESULTS.txt',
1 => '/home/hans/scraping/1650518121RESULTS.txt',
2 => '/home/hans/scraping/1650518679RESULTS.txt',
3 => '/home/hans/scraping/1650518780RESULTS.txt',
4 => '/home/hans/scraping/1650522198RESULTS.txt',
5 => '/home/hans/scraping/1650522927RESULTS.txt',
6 => '/home/hans/scraping/1650525391RESULTS.txt',
7 => '/home/hans/scraping/check_cache.php',
8 => '/home/hans/scraping/foo/bar.txt',
9 => '/home/hans/scraping/foobar.txt',
10 => '/home/hans/scraping/GoodProxyDaemon.php',
);
?>

(注意编辑,如果你能找到更好的示例返回值,请随意覆盖上面的示例)
dxvargas
4 年前
当使用 FilesystemIterator::FOLLOW_SYMLINKS 选项时,如果存在指向父目录的符号链接,就会出现循环,导致重复的目录。
例如(存在 -> ../)

/c
/c/..
/c/a
/c/a/c
/c/a/c/..
/c/a/c/a
/c/a/c/a/c
...(最多 40 x /c/a)
/c/a/c/a/..
/c/a/c/a/.
/c/a/c/.
/c/a/..
/c/a/.
/c/.
/..
/.

显然存在一个限制,以避免无限循环。我不知道对此的文档。

可以考虑添加一个选项,允许在不出现循环的情况下跟踪符号链接。
Edward Rudd
10 年前
(与关于 getChildren() 中异常的帖子有关。)

你可以简单地使用 RecursiveIteratorIterator 的 CATCH_GET_CHILD 标志,而不是进行子类化。

new RecursiveIteratorIterator($diriter, RecursiveIteratorIterator::CATCH_GET_CHILD);
flaurora_sonora
4 年前
不要浪费时间在这个上面。我已经在下面添加了一个函数,它以更简单的形式实现了相同的功能。如果你想在迭代时进行正则表达式匹配,可以将正则表达式作为参数传递给递归函数。

<?php

function recursive_read($directory, $entries_array = array()) {
if(
is_dir($directory)) {
$handle = opendir($directory);
while(
FALSE !== ($entry = readdir($handle))) {
if(
$entry == '.' || $entry == '..') {
continue;
}
$Entry = $directory . DS . $entry;
if(
is_dir($Entry)) {
$entries_array = recursive_read($Entry, $entries_array);
} else {
$entries_array[] = $Entry;
}
}
closedir($handle);
}
return
$entries_array;
}

?>
dblanchard1 at bbox dot fr
10 年前
如果你想递归地将源目录中的所有文件复制到某个目标目录

$directory = new RecursiveDirectoryIterator("./source_path/");

foreach (new RecursiveIteratorIterator($directory) as $filename=>$current) {

$src = $current->getPathName();
$dest = "./destination_path/" . $current->getFileName();

echo "copy " . $src . " => " . $dest . "\n";

copy($src, $dest);
}

我希望这对某人有所帮助,因为当我寻找这个解决方案时,我不得不转换另一个示例才能得到它。
rockerBOO
10 年前
当循环遍历 RecursiveDirectoryIterator 时,结果使用 SplFileInfo。
To Top