7

可能重复:
PHP SPL RecursiveDirectoryIterator RecursiveIteratorIterator 检索完整树

我不知道从哪里开始。但是我必须获取文件夹中所有文件的路径以及路径中子文件夹的所有内容。例如,如果我有 1 个文件夹,其中有 5 个文件夹,每个文件夹中有 10 个 mp3,等等……这意味着我的数组必须找到这些文件的 50 个路径。

后来假设我添加了一个文件夹,其中有 3 个文件夹,每个文件夹有 10 张图像。

我的代码现在需要找到 80 个路径并将它们存储在一个数组中。

我的问题有意义吗?

更新:

我想要的输出是将所有这些路径存储在一个数组中。

但是我会“喜欢”代码是动态的,这意味着如果我以后再添加 10 个文件夹,每个文件夹都有 17 个子文件夹,每个文件夹都有大量不同的内容。我希望数组保存所有文件的文件路径。我觉得这是有道理的。

4

3 回答 3

25

您正在寻找的也称为递归目录遍历。这意味着,您将浏览所有目录并列出其中的子目录和文件。如果有一个子目录,它也会被遍历等等 - 所以它是递归的。

正如您可以想象的那样,这是您在编写软件时所需要的一种常见的东西,而 PHP 会为您提供支持。它提供了一个RecursiveDirectoryIterator目录可以递归迭代和RecursiveIteratorIterator执行遍历的标准。然后,您可以通过简单的迭代轻松访问所有文件和目录,例如通过foreach

$rootpath = '.';
$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath)
);
foreach($fileinfos as $pathname => $fileinfo) {
    if (!$fileinfo->isFile()) continue;
    var_dump($pathname);
}

这个例子首先指定了你要遍历的目录。我一直在服用当前的:

$rootpath = '.';

下一行代码有点长,它确实实例化了目录迭代器,然后是迭代器迭代器,这样树状结构就可以在单个/平面循环中遍历:

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath)
);

$fileinfos然后用一个简单的迭代这些foreach

foreach($fileinfos as $pathname => $fileinfo) {

在其中,有一个测试可以跳过所有目录的输出。这是通过使用SplFileInfo被迭代的对象来完成的。它由递归目录迭代器提供,在处理文件时包含许多有用的属性和方法。例如,您还可以返回文件扩展名、关于大小和时间的基本名称信息等等。

if (!$fileinfo->isFile()) continue;

最后,我只输出作为文件完整路径的路径名:

var_dump($pathname);

示例输出如下所示(在 Windows 操作系统上):

string(12) ".\.buildpath"
string(11) ".\.htaccess"
string(33) ".\dom\xml-attacks\attacks-xml.php"
string(38) ".\dom\xml-attacks\billion-laughs-2.xml"
string(36) ".\dom\xml-attacks\billion-laughs.xml"
string(40) ".\dom\xml-attacks\quadratic-blowup-2.xml"
string(40) ".\dom\xml-attacks\quadratic-blowup-3.xml"
string(38) ".\dom\xml-attacks\quadratic-blowup.xml"
string(22) ".\dom\xmltree-dump.php"
string(25) ".\dom\xpath-list-tags.php"
string(22) ".\dom\xpath-search.php"
string(27) ".\dom\xpath-text-search.php"
string(29) ".\encrypt-decrypt\decrypt.php"
string(29) ".\encrypt-decrypt\encrypt.php"
string(26) ".\encrypt-decrypt\test.php"
string(13) ".\favicon.ico"

如果存在不可访问的子目录,则以下将引发异常。实例化时,可以使用一些标志来控制此行为RecursiveIteratorIterator

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('.'),
    RecursiveIteratorIterator::LEAVES_ONLY,
    RecursiveIteratorIterator::CATCH_GET_CHILD
);

我希望这是有益的。您还可以将其包装到您自己的类中,您还可以提供一个FilterIterator来决定是否应该列出文件foreach


RecursiveDirectoryIterator和组合的力量RecursiveIteratorIterator来自于它的灵活性。上面没有提到的是所谓FilterIterator的 s。我想我添加了另一个例子,它利用了两个自己编写的,将它们放在一起来组合它们。

  • 一种是过滤掉所有以点开头的文件和目录(在 UNIX 系统上这些文件和目录被视为隐藏文件,因此您不应将这些信息提供给外部)和
  • 另一个仅将列表过滤为文件的方法。那是以前foreach 中的检查。

此用法示例中的另一个更改是使用从迭代的根路径开始返回子路径的getSubPathname()函数,因此您正在寻找的那个。

此外,我明确添加了防止遍历的SKIP_DOTS标志...(技术上不是真正必要的,因为过滤器会过滤它们以及它们是目录,但我认为它更正确)并作为路径返回,UNIX_PATHS因此路径字符串始终是 unix-无论底层操作系统如何,都喜欢路径如果稍后通过 HTTP 请求这些值,这通常是一个好主意,就像您的情况一样:

$rootpath = '.';

$fileinfos = new RecursiveIteratorIterator(
    new FilesOnlyFilter(
        new VisibleOnlyFilter(
            new RecursiveDirectoryIterator(
                $rootpath,
                FilesystemIterator::SKIP_DOTS
                    | FilesystemIterator::UNIX_PATHS
            )
        )
    ),
    RecursiveIteratorIterator::LEAVES_ONLY,
    RecursiveIteratorIterator::CATCH_GET_CHILD
);

foreach ($fileinfos as $pathname => $fileinfo) {
    echo $fileinfos->getSubPathname(), "\n";
}

此示例与前一个示例类似,尽管$fileinfosis build 的配置方式略有不同。特别是关于过滤器的部分是新的:

    new FilesOnlyFilter(
        new VisibleOnlyFilter(
            new RecursiveDirectoryIterator($rootpath, ...)
        )
    ),

所以目录迭代器被放入一个过滤器中,过滤器本身被放入另一个过滤器中。其余的没有改变。

这些过滤器的代码非常简单,它们使用的accept函数要么是,要么truefalse要采取或过滤掉:

class VisibleOnlyFilter extends RecursiveFilterIterator
{
    public function accept()
    {
        $fileName = $this->getInnerIterator()->current()->getFileName();
        $firstChar = $fileName[0];
        return $firstChar !== '.';
    }
}

class FilesOnlyFilter extends RecursiveFilterIterator
{
    public function accept()
    {
        $iterator = $this->getInnerIterator();

        // allow traversal
        if ($iterator->hasChildren()) {
            return true;
        }

        // filter entries, only allow true files
        return $iterator->current()->isFile();
    }
}

又是这样。当然,您也可以将这些过滤器用于其他情况。例如,如果您有另一种目录列表。

还有另一个示例性的输出$rootpath

test.html
test.rss
tests/test-pad-2.php
tests/test-pad-3.php
tests/test-pad-4.php
tests/test-pad-5.php
tests/test-pad-6.php
tests/test-pad.php
TLD/PSL/C/dkim-regdom.c
TLD/PSL/C/dkim-regdom.h
TLD/PSL/C/Makefile
TLD/PSL/C/punycode.pl
TLD/PSL/C/test-dkim-regdom.c
TLD/PSL/C/test-dkim-regdom.sh
TLD/PSL/C/tld-canon.h
TLD/PSL/generateEffectiveTLDs.php

没有更多.git.svn目录遍历或文件列表,如.builtpath.project


FilesOnlyFilter和注意事项LEAVES_ONLY 过滤器明确拒绝使用基于对象的目录SplFileInfo链接(仅存在的常规文件)。所以它是一个真正的基于文件系统的过滤。由于默认标志(示例中也使用了此处) ,因此
附带了另一种仅获取非目录条目的方法。此标志不能用作过滤器,并且独立于底层迭代器。它只是指定迭代不应返回分支(此处:目录迭代器中的目录)。RecursiveIteratorIteratorLEAVES_ONLY

于 2012-09-02T06:50:31.373 回答
4

如果您在 linux 上并且不介意执行 shell 命令,则可以在一行中完成所有操作

$path = '/etc/php5/*'; // file filter, you could specify a extension using *.ext
$files = explode("\n", trim(`find -L $path`)); // -L follows symlinks

print_r($files);

输出:

Array (
       [0] => /etc/php5/apache2
       [1] => /etc/php5/apache2/php.ini
       [2] => /etc/php5/apache2/conf.d
       [3] => /etc/php5/apache2/conf.d/gd.ini
       [4] => /etc/php5/apache2/conf.d/curl.ini
       [5] => /etc/php5/apache2/conf.d/mcrypt.ini
       etc...
      )

仅使用 PHP 的下一个最短选择是 glob- 但它不会像您想要的那样扫描子目录。(您必须遍历结果,使用 is_dir() 然后再次调用您的函数

http://us3.php.net/glob

$files = dir_scan('/etc/php5/*'); 
print_r($files);

function dir_scan($folder) {
    $files = glob($folder);
    foreach ($files as $f) {
        if (is_dir($f)) {
            $files = array_merge($files, dir_scan($f .'/*')); // scan subfolder
        }
    }
    return $files;
}

其他所有方式都需要更多的代码,然后才需要做一些如此简单的事情

于 2012-09-02T06:54:53.280 回答
2

步骤如下:

和 opendir 将打开目录结构

$dh = opendir($dir)

你接下来要做的是阅读里面的任何东西$dh

$file = readdir($dh)

您可以在 php 手册中找到对应于的所有信息opendir

和谷歌搜索阅读结构返回这个

http://www.codingforums.com/showthread.php?t=71882

于 2012-09-02T06:27:37.797 回答