2012-09-02 4 views
7

Eventuali duplicati:
PHP SPL RecursiveDirectoryIterator RecursiveIteratorIterator retrieving the full treePHP ottiene il percorso di ogni file nella cartella/sottocartella nell'array?

non sono sicuro da dove cominciare. Ma devo trovare i percorsi per tutti i file in una cartella e anche tutti i contenuti della sottocartella nei percorsi. Ad esempio se avessi una cartella con cinque cartelle e ogni cartella contenesse 10 mp3s ecc ... Ciò significa che il mio array dovrebbe trovare 50 percorsi per questi file.

In seguito diciamo che ho aggiunto un'altra cartella e conteneva 3 cartelle e ogni cartella conteneva 10 immagini.

Ora il mio codice deve trovare 80 percorsi e memorizzarli in un array.

La mia domanda ha senso?

UPDATE:

mio put desiderato fuori sarebbe quello di avere tutti questi percorsi memorizzati in un array.

Ma vorrei "AMARE" il codice per essere dinamico, nel senso che se aggiungo in seguito altre 10 cartelle e ciascuna con 17 sottocartelle e ogni cartella con una moltitudine di contenuti diversi. Vorrei che l'array conservasse i percorsi dei file di tutti i file. Ho capito questo ha senso.

+1

Capisco la struttura della cartella che hai, ora che cosa vuoi che il tuo output sia. Aggiorna il ** output desiderato ** nella tua domanda! :) –

+0

perché vorresti farlo. Ho poca esperienza con il php ma a mio avviso ucciderebbe il tuo povero server. Immagina 5000 persone che leggono la tua struttura di directory !!! – Tanmay

+0

Beh, perché voglio farlo è perché ho uno script in flash as3 che può scaricare un file alla volta. Flash non può scaricare una cartella e il suo contenuto, quindi vorrei che il php crei una stringa di tutti i contenuti delle cartelle/sottocartelle e invii il messaggio a Flash e può iniziare a scaricare il contenuto nell'app. :) –

risposta

23

Quello che state cercando è chiamato anche directory ricorsiva attraversamento. Il che significa che stai esaminando tutte le directory e elenchi sottodirectory e file. Se c'è una sottodirectory viene anche attraversata e così via e così via - quindi è ricorsiva.

Come potete immaginare questo è un po 'una cosa comune di cui avete bisogno quando scrivete un software e PHP vi supporta con quello. Offre uno RecursiveDirectoryIterator in modo che le directory possano essere ripetute ripetutamente e lo standard RecursiveIteratorIterator per eseguire l'attraversamento. È quindi possibile accedere facilmente a tutti i file e le directory con un semplice iterazione, ad esempio attraverso foreach:

$rootpath = '.'; 
$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath) 
); 
foreach($fileinfos as $pathname => $fileinfo) { 
    if (!$fileinfo->isFile()) continue; 
    var_dump($pathname); 
} 

Questo esempio prima di tutto specifica la directory che si desidera percorrere.Ho preso quella attuale:

$rootpath = '.'; 

La successiva linea di codice è un po 'lunga, non un'istanza the directory iterator e poi the iterator-iterator modo che la struttura ad albero può essere attraversato in un singolo ciclo/piatto :

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootpath) 
); 

Questi $fileinfos vengono poi iterati con un semplice foreach:

foreach($fileinfos as $pathname => $fileinfo) { 

Dentro di esso, c'è un test per saltare tutte le directory dall'output. Questo viene fatto utilizzando l'oggetto SplFileInfo su cui è stata ripetuta l'iterazione. Viene fornito dall'iteratore di directory ricorsivo e contiene molte proprietà e metodi utili quando si lavora con i file. Puoi anche ad esempio restituire l'estensione del file, le informazioni sul nome di base su dimensioni e tempo e così via e così via.

if (!$fileinfo->isFile()) continue; 

Infine ho appena uscita la percorso che è il percorso completo del file:

var_dump($pathname); 

Un'uscita esemplare sarebbe simile a questa (qui su un sistema operativo 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" 

Se è presente una sottodirectory non accessibile, la seguente operazione genera un'eccezione. Questo comportamento può essere controllato con alcune bandiere quando si crea un'istanza della RecursiveIteratorIterator:

$fileinfos = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('.'), 
    RecursiveIteratorIterator::LEAVES_ONLY, 
    RecursiveIteratorIterator::CATCH_GET_CHILD 
); 

Spero che questo è stata istruttiva. Puoi anche eseguire il wrapping di questo in una classe personalizzata e puoi anche fornire un FilterIterator per decidere se un file debba essere elencato o meno dal ciclo foreach.


La potenza della combinazione RecursiveDirectoryIterator e RecursiveIteratorIterator esce dalla sua flessibilità. Ciò che non era coperto sopra sono i cosiddetti FilterIterator s. Ho pensato di aggiungere un altro esempio che sta facendo uso di due di loro auto-scritti, messi l'uno nell'altro per combinarli.

  • Uno è quello di filtrare tutti i file e le directory che iniziano con un punto (quelli sono considerati file nascosti sui sistemi UNIX, quindi non si dovrebbe dare tali informazioni al di fuori) e
  • Un altro che sta filtrando il elenca solo i file. Questo è il controllo che in precedenza era all'interno di il foreach.

Un altro cambiamento in questo esempio di utilizzo è quello di utilizzare il getSubPathname() function che restituisce il sottotracciato a partire dal ROOTPATH ​​del iterazione, in modo da quello che stai cercando.

Inoltre ho esplicitamente aggiungere il SKIP_DOTS flag che impedisce l'attraversamento . e .. (tecnicamente non davvero necessario in quanto i filtri sarebbero filtrare quelli così come essi sono directory, ma penso che sia più corretto) e di ritorno, percorsi come UNIX_PATHS così le corde di sentieri sono percorsi sempre unix-like indipendentemente dal sistema operativo sottostante che è normalmente una buona idea se questi valori vengono richiesti tramite HTTP in seguito come nel tuo caso:

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

Questo esempio è simile al precedente uno anche se come lo $fileinfos è costruito è un po 'diversamente configurato. Soprattutto la parte sui filtri è nuovo:

new FilesOnlyFilter(
     new VisibleOnlyFilter(
      new RecursiveDirectoryIterator($rootpath, ...) 
     ) 
    ), 

Così l'iteratore directory viene messo in un filtro e il filtro stesso viene messo in un altro filtro. Il resto non è cambiato.

Il codice per questi filtri è piuttosto semplice, lavorano con la funzione accept che è o true o false che è quello di prendere o di filtrare:

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

E questo è tutto nuovo. Naturalmente puoi usare questi filtri anche per altri casi. Per esempio. se hai un altro tipo di elenco di directory.

E un'altra uscita esemplare con la $rootpath tagliato via:

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 

Non più .git o .svn directory traversal o elenco di file come .builtpath o .project.


Nota per FilesOnlyFilter e LEAVES_ONLY: Il filtro nega esplicitamente l'uso delle directory e collegamenti basati sull'oggetto SplFileInfo (only regular files that do exist). Quindi è un vero filtro basato sul file system.
Un altro metodo per ottenere solo voci non di directory viene fornito con RecursiveIteratorIterator a causa del valore predefinito LEAVES_ONLY flag (qui utilizzato anche negli esempi). Questo flag non funziona come filtro ed è indipendente dall'iteratore sottostante. Specifica solo che l'iterazione non deve restituire le diramazioni (qui: directory in caso di iteratore di directory).

+0

INCREDIBILE! Wow! ok come potrei ottenere solo il pathname senza la stringa (33) su di esso ecc. e come posso ottenere anche solo il nome del file? :) –

+0

Anche il nome del percorso meno il ./ appena prima di esso? :) –

+0

Ci sono molti modi per farlo. È possibile "estendere" questo con alcuni post-processing all'interno degli oggetti 'foreach', [[SplFileInfo'] (http://php.net/SplFileInfo) qui, sono disponibili in' $ fileinfo'. Per rimuovere il basedir è in realtà banale perché è '$ rootpath' più il separatore di directory del file system la cui lunghezza è normalmente esattamente un carattere. Quindi hai il 'substr ($ pathname, 2)' qui. Estenderò la risposta con un altro esempio di filtro e aggiungerò l'operazione di sottostringa esemplare. – hakre

2

I passaggi sono i quali:

e opendir apriranno la struttura di directory

$dh = opendir($dir)

ciò che si fa prossimo è letto tutto ciò che è lì in $dh

$file = readdir($dh)

voi puoi trovare tutte le informazioni nel manuale php corrispondente a opendir

e googling per la lettura della struttura restituita questa

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

+0

Impressionante. Grazie. sembra fantastico. Provalo adesso :) Tornerai da te. –

+0

nessun problema. Godere! – Tanmay

+1

eccessivamente complicato per qualcosa di così semplice, puoi farlo in 5 righe di codice (7 con parentesi per leggibilità) vedi la mia risposta sotto per il codice esatto che ti serve – RobertMaysJr

4

Se siete su Linux e non si mente l'esecuzione di un comando di shell, si può fare tutto questo in una sola riga

$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); 

uscita:

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... 
    ) 

La prossima scelta più breve utilizzando solo PHP è glob, ma non esegue la scansione delle sottodirectory come vuoi.(Dovreste scorrere i risultati, utilizzare is_dir() e quindi chiamare la funzione di nuovo

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

Ogni altro modo richiede molto più codice allora dovrebbe essere necessario fare qualcosa di così semplice

+0

Punto rilevato. Grazie! – Tanmay

+1

:-) Nessun problema. un'altra cosa interessante di glob è che puoi specificare un filtro diverso (* .txt per esempio) e fare tutto il filtraggio dei file contemporaneamente (non solo eviti di dover analizzare ogni nome di file per controllare le estensioni, non lo fai anche doverli passare inosservati perché sono già filtrati) – RobertMaysJr

+0

C'è un modo per ottenere meno il ./ prima in ogni array? Inoltre posso ottenere un array di soli nomi di file senza percorso e ho estensione? Questo è geniale. –