2

具有以下代码段以递归方式映射当前目录的内容:

$files = new RecursiveIteratorIterator
(
    new RecursiveDirectoryIterator('./',
        FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS),
    RecursiveIteratorIterator::SELF_FIRST
);

$files = array_values(array_map('strval', iterator_to_array($files)));

返回如下内容:

Array
(
    [0] => ./1.png
    [1] => ./a.php
    [2] => ./adminer
    [3] => ./adminer/adminer.css
    [4] => ./adminer/adminer.php
)

有什么方法可以获取RecursiveDirectoryIterator/FilesystemIterator模拟函数GLOB_MARK中存在的行为glob()?从手册:

GLOB_MARK - 在返回的每个目录中添加一个斜杠。

我知道我可以通过简单地模仿它:

foreach ($files as $key => $value)
{
    $files[$key] .= (is_dir($value) ? '/' : '');
}

但这需要(不必要地?)多次访问磁盘。我正在寻找一种方法来快速确定路径是目录还是常规文件,而结尾的斜杠似乎是理想的解决方案。

我计划用这个遍历数万(如果不是数百)数千个文件,因此性能至关重要。

奖励问题:有没有办法只获取目录(递归)?

4

2 回答 2

4

您可以创造自己的GlobMarkIterator优势:

  • 将结束斜杠返回到目录,就像GLOB_MARK
  • 无需使用array_mapwithstrval将其转换为字符串
  • 没有额外的foreach循环is_dir
  • 还是和原来一样快
  • 是的,我知道我作弊了

例子

$ri = new RecursiveIteratorIterator(new GlobMarkIterator('./', FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS), RecursiveIteratorIterator::SELF_FIRST);
$files = array_values(iterator_to_array($ri));

echo "<pre>";
print_r($files);

输出

Array
(
    [0] => ./test/backups/ <----------- Note ending slash 
    [1] => ./test/CSV/
    [2] => ./test/CSV/abc.csv
    [3] => ./test/final/
    [4] => ./test/thumb/
    [5] => ./test/thumb/a.png
    [6] => ./test/thumb/s.svg
    [7] => ./test/thumb/sample.svg
)



奖励问题:有没有办法只获取目录(递归)?

这应该是另一个问题,但都是一样的......我希望你不要满意,并为此提供赏金

解决方案 :

$ri = new RecursiveIteratorIterator( new GlobMarkDirectory('./test'), RecursiveIteratorIterator::SELF_FIRST);
$dir = array_values(iterator_to_array($ri));

echo "<pre>";
print_r($dir);

输出

Array
(
    [0] => ./test/backups/
    [1] => ./test/CSV/
    [2] => ./test/final/
    [3] => ./test/thumb/
)

使用的类

GlobMarkIterator

class GlobMarkIterator extends RecursiveDirectoryIterator {
    function current() {
        return $this->isDir() ? $this->getPathname() . "/" : $this->getPathname();
    }
}

GlobMarkDirectory班级

class GlobMarkDirectory  extends RecursiveFilterIterator {
    public function __construct($path) {
        parent::__construct(new GlobMarkIterator($path, FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS));
    }
    public function accept() {
        return $this->getInnerIterator()->isDir();
    }
    public function getChildren() {
        return new GlobMarkDirectory($this->getInnerIterator()->getPathname());
    }
}



编辑..如果您不关心并且由于速度和开销而empty dir不想使用这里是另一种解决方案isDir

解决方案

$ri = new RecursiveIteratorIterator(new GlobMarkFastDirectory  (__DIR__), RecursiveIteratorIterator::SELF_FIRST);
$dir = array_values(array_unique(iterator_to_array($ri)));

GlobMarkFastDirectory

class GlobMarkFastDirectory  extends RecursiveDirectoryIterator {
    function current() {
        return dirname($this->getPathname())  ."/";
    }
}
于 2012-10-20T12:45:12.457 回答
1

这是我迄今为止的最大努力:

$files = new RecursiveIteratorIterator
(
    new RecursiveDirectoryIterator
    (
        str_replace('\\', '/', realpath('./')),
        FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS
    ),
    RecursiveIteratorIterator::LEAVES_ONLY
);

$files = array_keys(iterator_to_array($files));
$folders = array();

/*
foreach ($files as $key => $value) // not needed anymore
{
    $files[$key] .= (is_dir($value) === true) ? '/' : '';
}
*/

$files = array_flip($files);

foreach ($files as $key => $value)
{
    $folder = dirname($key) . '/'; // doesn't issue a stat call

    if (array_key_exists($folder, $folders) !== true)
    {
        $folders[$folder] = 0;
    }

    $folders[$folder] += $files[$key] = sprintf('%u', filesize($key));
}

我可以将$folders数组与$files数组合并以准确回答问题,但区分另一个正是我的主要目标,所以这样做没有意义。

于 2012-10-20T14:32:15.907 回答