7

重複の可能性:
PHP SPL RecursiveDirectoryIterator RecursiveIteratorIterator ツリー全体を取得する

どこから始めればよいかわかりません。しかし、フォルダー内のすべてのファイルへのパスと、パス内のサブフォルダーのすべてのコンテンツも取得する必要があります。たとえば、5 つのフォルダーを持つ 1 つのフォルダーがあり、各フォルダーに 10 個の mp3 ファイルがある場合など...つまり、アレイはこれらのファイルへの 50 個のパスを見つける必要があります。

後で、もう 1 つのフォルダーを追加し、その中に 3 つのフォルダーがあり、各フォルダーに 10 個の画像があるとします。

私のコードでは、80 個のパスを見つけて配列に格納する必要があります。

私の質問は理にかなっていますか?

アップデート:

私の望む出力は、これらすべてのパスを 1 つの配列に格納することです。

しかし、私はコードが動的であることを「愛しています」。配列にすべてのファイルのファイル パスを保持したいと思います。これは理にかなっていると思います。

4

3 に答える 3

25

あなたが探しているものは、recursive directory traversingとも呼ばれます。つまり、すべてのディレクトリを調べて、そこにあるサブディレクトリとファイルをリストします。サブディレクトリがある場合は、それも同様にトラバースされます。つまり、再帰的です。

ご想像のとおり、これはソフトウェアを作成するときに必要となる一般的なものであり、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その柔軟性から生まれます。上記でカバーされていないものは、いわゆるFilterIterators です。私は、それらを組み合わせるために互いに配置された2つの自己記述を利用している別の例を追加すると思いました.

  • 1 つは、ドットで始まるすべてのファイルとディレクトリを除外することです (これらは UNIX システムでは隠しファイルと見なされるため、その情報を外部に提供しないでください)。
  • リストをファイルのみにフィルタリングしている別のもの。これは、以前はforeachにあったチェックです。

この使用例のもう 1 つの変更点は、反復のルートパスから始まるサブパスを返すgetSubPathname()関数を使用することです。つまり、探しているものです。

また、トラバースを防止するSKIP_DOTSフラグを明示的に追加し(技術的には、フィルターがそれらとディレクトリをフィルター処理するため、実際には必要ありませが、より正確だと思います)、パスとして返すため、パスの文字列は常に unix-基礎となるオペレーティングシステムに関係なくパスのように、これらの値が後でHTTP経由で要求された場合、通常は良い考えです:...UNIX_PATHS

$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関数で動作します。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 を使用していて、シェル コマンドの実行を気にしない場合は、これをすべて 1 行で実行できます。

$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 に答える