可能重复:
PHP SPL RecursiveDirectoryIterator RecursiveIteratorIterator retrieving the full tree
我不知道从哪里开始。但我必须获取文件夹中所有文件的路径以及路径中子文件夹的所有内容。例如,如果我有一个文件夹有五个文件夹,每个文件夹中有10个mp3等等...这意味着我的数组必须找到50个这些文件的路径。
稍后我说我添加了一个文件夹,其中有3个文件夹,每个文件夹有10个图像。
我的代码现在需要找到80个路径并将它们存储在一个数组中。
我的问题有意义吗?
更新:
我想要的输出是将所有这些路径存储在一个数组中。
但我会“爱”代码是动态的,这意味着如果我以后再添加10个文件夹,每个文件夹有17个子文件夹,每个文件夹都有大量不同的内容。我希望数组保存所有文件的文件路径。我知道这是有道理的。
答案 0 :(得分: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 = '.';
下一行代码有点长,它实例化the directory iterator然后the iterator-iterator,以便可以在单个/平坦循环中遍历树状结构:
$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
。我想我添加了另一个使用两个自编的例子,相互放在一起组合起来。
此用法示例中的另一个更改是使用从迭代的根路径开始返回子路径的getSubPathname()
function,这是您要查找的路径。
此外,我明确添加了SKIP_DOTS
flag,以防止遍历.
和..
(技术上不是真正必要,因为过滤器会过滤掉那些以及它们是目录,但我认为它更正确)并返回路径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";
}
此示例与前一个示例类似,尽管构建$fileinfos
的方式稍有不同。特别是关于过滤器的部分是新的:
new FilesOnlyFilter(
new VisibleOnlyFilter(
new RecursiveDirectoryIterator($rootpath, ...)
)
),
因此目录迭代器被放入过滤器,过滤器本身被放入另一个过滤器。其余的没有改变。
这些过滤器的代码非常简单,它们与accept
或true
的{{1}}或false
函数一起使用,该函数将采用或过滤掉:
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
对象(only regular files that do exist)使用目录和链接。所以这是一个基于文件系统的真正过滤 另一种仅获取非目录条目的方法附带RecursiveIteratorIterator
,因为默认LEAVES_ONLY
flag(这里也使用了示例)。此标志不能用作过滤器,并且与底层迭代器无关。它只是指定迭代不应该返回分支(在目录迭代器的情况下,这里:目录)。
答案 1 :(得分: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()然后再次调用你的函数
$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;
}
其他方式需要更多代码,然后才需要做这么简单的事情
答案 2 :(得分:2)
步骤如下:
和opendir将打开目录结构
$dh = opendir($dir)
您接下来要做的是阅读$dh
$file = readdir($dh)
您可以在php手册中找到与opendir
和谷歌搜索结构返回此