使用FilesystemIterator将尾随斜杠应用于文件夹

时间:2012-10-20 12:00:04

标签: php filesystems iterator spl

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

$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()函数中存在的GLOB_MARK行为?从手册:

  

GLOB_MARK - 为返回的每个目录添加斜杠。

我知道我可以通过简单地做到:

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

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

我打算用这个来遍历数十个(如果不是数百个)数千个文件,因此性能至关重要。

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

2 个答案:

答案 0 :(得分:4)

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

  • GLOB_MARK
  • 返回结束斜杠到目录
  • 无需将array_mapstrval一起使用即可将其转换为字符串
  • 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())  ."/";
    }
}

答案 1 :(得分: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数组合并,以准确地回答这个问题,但区分其中一个恰好是我的主要目标,所以没有必要这样做。