2012-09-02 28 views
7

可能重複:
PHP SPL RecursiveDirectoryIterator RecursiveIteratorIterator retrieving the full treePHP獲取文件夾/子文件夾中的每個文件到數組的路徑?

我不知道從哪裏開始。但是我必須獲取文件夾中所有文件的路徑以及路徑中子文件夾的所有內容。例如,如果我有一個文件夾有五個文件夾,每個文件夾有10個mp3文件等等,這意味着我的數組必須找到50個這些文件的路徑。

後來讓我說我增加了一個文件夾,它有3個文件夾,每個文件夾有10個圖像。

我的代碼現在需要找到80個路徑並將它們存儲在一個數組中。

我的問題有意義嗎?

UPDATE:

我的期望了放將具有存儲在一個陣列中的所有這些路徑。

但我會「愛」代碼是動態的,這意味着如果我以後再添加10個文件夾,每個文件夾有17個子文件夾和每個文件夾有不同的內容。我希望數組保存所有文件的文件路徑。我知道這是有道理的。

+1

我明白你的文件夾結構,現在你想要輸出什麼,在你的問題中更新**所需的輸出** :) –

+0

你爲什麼要這麼做。我沒有使用PHP的經驗,但在我看來,這會殺死你可憐的服務器。想象一下5000人正在讀你的目錄結構! – Tanmay

+0

那麼爲什麼我想這樣做是因爲我有一個腳本在閃存as3,可以一次下載一個文件。 Flash無法下載文件夾及其內容,所以我想讓php創建一個包含文件夾/子文件夾所有內容的字符串,並將其發送回閃存,並且可以開始下載應用程序中的內容。 :) –

回答

23

你所尋找的是也被稱爲遞歸遍歷目錄。這意味着,您正在瀏覽所有目錄並列出其中的子目錄和文件。如果有一個子目錄被遍歷,等等等等 - 所以它是遞歸的。

正如你可以想象的那樣,當你編寫一個軟件並且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循環之外的決定。


RecursiveDirectoryIteratorRecursiveIteratorIterator組合的力量散發出來的靈活性。以上沒有提到的是所謂的FilterIterator。我想我補充了另一個利用兩個自己編寫的例子,將它們放置在一起以便合併它們。

  • 一是篩選出以點開始(那些被認爲是UNIX系統上的隱藏文件,所以你不應該給這些信息外)和
  • 另外一個是過濾所有的文件和目錄僅列出文件。那就是之前的這個檢查。

在此使用示例中的另一個變化是使用它返回迭代的ROOTPATH啓動子路徑的getSubPathname() function的,所以一個你要尋找的。

而且我明確地添加,防止穿越...SKIP_DOTS flag(技術上沒有真的有必要因爲過濾器將過濾那些,以及他們是目錄,但我認爲這是比較正確的),並返回的路徑,UNIX_PATHS這樣的路徑字符串是不管底層操作系統通常是,如果通過HTTP請求這些值後來由於你的情況是個好主意總是Unix類路徑:

$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文件的清單。


注意事項FilesOnlyFilterLEAVES_ONLY 過濾明確否認使用基於SplFileInfo對象(only regular files that do exist)上的目錄鏈接。所以這是一個基於文件系統的真正的過濾。
由於默認LEAVES_ONLY flag(這裏也在示例中使用),另一種僅獲得非目錄條目的方法是RecursiveIteratorIterator。該標誌不起濾波器的作用,獨立於底層迭代器。它只是指定迭代不應該返回分支(這裏:目錄的情況下,目錄迭代器)。

+0

令人驚歎!哇!好的,我如何得到沒有字符串(33)的路徑名等等,以及我怎樣才能得到文件名呢? :) –

+0

也是路徑名減去./之前的權利? :) –

+0

有多種方法可以實現這一點。你可以通過'foreach'中的一些後處理來「擴展」這個,['SplFileInfo'](http://php.net/SplFileInfo)對象在這裏很有幫助,你可以在'$ fileinfo'中找到。刪除basedir實際上是微不足道的,因爲它是'$ rootpath'加上文件系統目錄分隔符,其長度通常只是一個字符。所以你在這裏有'substr($ pathname,2)'。我將用另一個過濾器示例擴展答案,並將添加該子串操作示例。 – hakre

2

操作步驟,例如:

和執行opendir打開的目錄結構

$dh = opendir($dir)

你接下來讀什麼不管有沒有在$dh

$file = readdir($dh)

你可以找到對應於的php手冊中的所有信息

,並使用Google讀取結構返回此

http://www.codingforums.com/showthread.php?t=71882

+0

太棒了。謝謝。看起來很棒。現在測試它:)將回到你身邊。 –

+0

沒問題。請享用! – Tanmay

+1

對於如此簡單的事情過於複雜,您可以在5行代碼中執行此操作(爲了方便閱讀,請使用括號7)請參閱下面的答案,以獲取您需要的確切代碼 – RobertMaysJr

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(),然後再調用你的函數

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; 
} 

其他各方面需要更多的方式代碼,那麼應該有必要做這麼簡單的東西

+0

注意點。謝謝! – Tanmay

+1

:-)沒問題。關於glob的另一件很酷的事情是你可以指定一個不同的過濾器(例如* .txt)並且同時進行所有文件過濾(不僅避免了必須解析每個文件名來檢查擴展名,甚至必須循環它們,因爲它們已經被過濾了) – RobertMaysJr

+0

有沒有辦法在每個數組中減去./之前的值?我也可以得到一個沒有路徑和ho擴展名的文件名的數組嗎?這是輝煌的順便說一句。 –

相關問題