PHP中深度递归的目录结构数组

19
我试图将硬盘上的一些文件夹放入数组中。
例如,假设我们有以下结构:
Set 1 - Item 1 of Set 1 - Item 2 of Set 1 - Item ... of Set 1
Set 2 - Subset 1 of Set 2 - Item 1 of Subset 1 of Set 2 - Item ... of Subset 1 of Set 2 - Subset 2 of Set 2 - Random file,不是一个目录。
我想要像这样的数组。意思是我有一个大数组,在该数组中有更多的数组。每个集和子集都有自己的数组。
我试图让它看起来像这样:
Array
(
    [Set 1] => Array([0] => Item 1 of Set 1, [1] => Item 1 of Set 1,...)
    [Set 2] => Array([Subnet 1] => Array([0] => Item 1 of Subset 1 of Set 2,[1] => ...), [Subnet 2] => Array([0] => ..., ..., ...), ..., [0] => Random File)
    [set 3] => Array(...)
    ...
)

我发现这个链接:http://www.the-art-of-web.com/php/dirlist/

但这不是我要找的。我已经研究了它,但只给我带来了麻烦。

这里有一个示例,查看源代码以获取更大的分辨率(显然不能点击...)。 Example


这个问题不是重复的,不应该被标记为重复。 - Dom
6个回答

47
我建议使用DirectoryIterator来构建你的数组。
这是我匆忙拼凑的一小段代码,但目前我没有测试环境,所以准备好进行调试。
$fileData = fillArrayWithFileNodes( new DirectoryIterator( '/path/to/root/' ) );

function fillArrayWithFileNodes( DirectoryIterator $dir )
{
  $data = array();
  foreach ( $dir as $node )
  {
    if ( $node->isDir() && !$node->isDot() )
    {
      $data[$node->getFilename()] = fillArrayWithFileNodes( new DirectoryIterator( $node->getPathname() ) );
    }
    else if ( $node->isFile() )
    {
      $data[] = $node->getFilename();
    }
  }
  return $data;
}

1
好的,我有机会运行这个程序并修复了错误 - 再试一次。 - Peter Bailey
现在发生的是,每当我进入一个新的主文件夹时,旧的文件夹就会被覆盖。 - KdgDev
1
你最初的问题询问如何递归地遍历目录树并将那些数据加载到一个数组中 - 我的回答就是这样做的。如果你期望得到更多的东西,你就需要提供更多的细节。 - Peter Bailey
1
彼得的解决方案看起来与你的白板所示完全一致。问题出在哪里? - rojoca
对我来说,当添加utf-8编码时,外国字符可以正常工作: $data[] = utf8_encode($node->getFilename()); 当在类中使用该函数时,我还必须添加$this: $data[$node->getFilename()] = $this->fillArrayWithFileNodes(... - Ralf
显示剩余7条评论

14

一个简单的实现,没有任何错误处理:

function dirToArray($dir) {
    $contents = array();
    # Foreach node in $dir
    foreach (scandir($dir) as $node) {
        # Skip link to current and parent folder
        if ($node == '.')  continue;
        if ($node == '..') continue;
        # Check if it's a node or a folder
        if (is_dir($dir . DIRECTORY_SEPARATOR . $node)) {
            # Add directory recursively, be sure to pass a valid path
            # to the function, not just the folder's name
            $contents[$node] = dirToArray($dir . DIRECTORY_SEPARATOR . $node);
        } else {
            # Add node, the keys will be updated automatically
            $contents[] = $node;
        }
    }
    # done
    return $contents;
}

这里发生的是目录列表被扁平化,所有文件都会一个接一个地列出来。 - KdgDev
我刚刚运行了音乐编译的一部分,结果如预期。 - soulmerge
我知道这已经有点老了,但这正是我一直在寻找的。完美无瑕。谢谢! - Shelly
非常好,是的。我进行了一些小调整。大多数情况下使用DIRECTORY_ITERATOR并不是必要的。哦,天哪,评论编辑器不允许我格式化。唉。我会在下面添加我的代码片段。 - tim
@tim 提醒一下,@soulmerge的代码中没有DIRECTORY_ITERATOR... (DIRECTORY_SEPARATOR ≠ DIRECTORY_ITERATOR) - e-sushi

6

基于@soulmerge的回答的代码。我只是删除了一些细节和注释,并使用$startpath作为我的起始目录。(感谢@soulmerge!)

function dirToArray($dir) {
    $contents = array();
    foreach (scandir($dir) as $node) {
        if ($node == '.' || $node == '..') continue;
        if (is_dir($dir . '/' . $node)) {
            $contents[$node] = dirToArray($dir . '/' . $node);
        } else {
            $contents[] = $node;
        }
    }
    return $contents;
}

$r = dirToArray($startpath);
print_r($r);

2

我使用过PEAR File_Find包,特别是mapTreeMultiple()方法,取得了成功。你的代码将变成如下形式:

require_once 'File/Find.php';
$fileList = File_Find::mapTreeMultiple($dirPath);

这应该返回一个类似于您所要求的数组。

这个 'File/Find.php' 文件是什么?我已经扫描了我的硬盘,但没有找到这个文件。我已经安装了 PEAR。可能是因为我在使用 Windows。 - KdgDev

1

我想指出一个可能会让人感到惊讶的事实,即结果表中的索引非常重要。本文介绍的解决方案使用序列:

    $contents[$node] = ...
                       ...
    $contents[] =      ...

当目录名只包含数字时,使用该代码将会生成意外的结果。 例如:

/111000/file1 
       /file2
/700030/file1 
       /file2
       /456098
             /file1 
             /file2 
/999900/file1
       /file2
/file1
/file2

结果:

Array
(
    [111000] => Array
        (
            [0] => file1
            [1] => file2
        )

    [700030] => Array
        (
            [456098] => Array
                (
                    [0] => file1
                    [1] => file2
                )

            [456099] => file1 <---- someone can expect 0
            [456100] => file2 <---- someone can expect 1
        )

    [999900] => Array
        (
            [0] => file1
            [1] => file2
        )

    [999901] => file1   <---- someone can expect 0
    [999902] => file2   <---- someone can expect 1
)

如您所见,4个元素的索引是目录最后一个名称的递增序列。

0

6年后....

我需要像@tim和@soulmerge的答案一样的解决方案。我试图为默认的codeigniter文件夹中的所有php文件创建批量php UnitTest模板。

这是我的第一步,获取目录/文件夹的完整递归内容作为数组。然后重新创建文件结构,在目录内有具有正确名称、类结构、括号、方法和注释部分的文件,准备填写php UnitTest。

总结一下:给出一个目录名,得到一个单层次的数组,其中所有目录作为键,所有文件作为值。

我进一步扩展了他们的答案,你会得到以下结果:

function getPathContents($path)
{
     // was a file path provided?
     if ($path === null){
        // default file paths in codeigniter 3.0
        $dirs = array(
            './models',
            './controllers'
        );
    } else{
        // file path was provided
        // it can be a comma separated list or singular filepath or file
        $dirs = explode(',', $path);
    }
    // final array
    $contents = array();
    // for each directory / file given
    foreach ($dirs as $dir) {
        // is it a directory?
        if (is_dir($dir)) {
            // scan the directory and for each file inside
            foreach (scandir($dir) as $node) {
                // skip current and parent directory
                if ($node === '.' || $node === '..') {
                    continue;
                } else {
                    // check for sub directories
                    if (is_dir($dir . '/' . $node)) {
                        // recursive check for sub directories
                        $recurseArray = getPathContents($dir . '/' . $node);
                        // merge current and recursive results
                        $contents = array_merge($contents, $recurseArray);
                    } else {
                        // it a file, put it in the array if it's extension is '.php'
                        if (substr($node, -4) === '.php') {
                            // don'r remove periods if current or parent directory was input
                            if ($dir === '.' || $dir === '..') {
                                $contents[$dir][] = $node;
                            } else {
                                // remove period from directory name 
                                $contents[str_replace('.', '', $dir)][] = $node;
                            }
                        }
                    }
                }
            }
        } else {
            // file name was given
            $contents[] = $dir; 
        }
    }
    return $contents;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接