2010-07-23 10 views
22

Todavía tengo que encontrar un buen ejemplo de cómo usar el RegexIterator php para recorrer recursivamente un directorio.Cómo usar RegexIterator en PHP

El resultado final sería que quiero especificar un directorio y encontrar todos los archivos en él con algunas extensiones dadas. Digamos, por ejemplo, solo extensiones html/php. Por otra parte, quiero filtrar las carpetas como del tipo .Trash-0, .Trash-500, etc.

<?php 
$Directory = new RecursiveDirectoryIterator("/var/www/dev/"); 
$It = new RecursiveIteratorIterator($Directory); 
$Regex = new RegexIterator($It,'/^.+\.php$/i',RecursiveRegexIterator::GET_MATCH); 

foreach($Regex as $v){ 
    echo $value."<br/>"; 
} 
?> 

es lo que tengo hasta ahora, pero como resultado: Fatal error: Excepción no detectada 'UnexpectedValueException' con el mensaje 'RecursiveDirectoryIterator :: __ construct (/media/hdmovies1/.Trash-0)

¿Alguna sugerencia?

Respuesta

46

Hay un par de maneras diferentes de ir sobre algo como esto, voy a dar dos enfoques rápidos para que usted pueda elegir: rápido y sucio, frente larga y menos sucio (aunque, es un viernes por la noche por lo que' re permitido ir un poco loco).

1. Rápida (y sucio)

Esto implica sólo escribir una expresión regular (podría ser dividida en múltiples) para filtrar la colección de archivos de un solo golpe rápido.

(solo las dos líneas de comentarios son muy importantes para el concepto.)

$directory = new RecursiveDirectoryIterator(__DIR__); 
$flattened = new RecursiveIteratorIterator($directory); 

// Make sure the path does not contain "/.Trash*" folders and ends eith a .php or .html file 
$files = new RegexIterator($flattened, '#^(?:[A-Z]:)?(?:/(?!\.Trash)[^/]+)+/[^/]+\.(?:php|html)$#Di'); 

foreach($files as $file) { 
    echo $file . PHP_EOL; 
} 

Este enfoque tiene una serie de problemas, a pesar de que es rápido de implementar ser sólo una sola línea (aunque el regex puede ser un dolor para descifrar).

2. Menos rápida (y menos sucio)

Un reutilizable enfoque más es crear un par de filtros a medida (usando expresiones regulares, o lo que quiera!) Para reducir gradualmente la lista de los disponibles artículos en la inicial RecursiveDirectoryIterator hasta solo aquellos que desee. El siguiente es solo un ejemplo, escrito rápidamente solo para usted, de extender el RecursiveRegexIterator.

Comenzamos con una clase base cuyo trabajo principal es retener la expresión regular con la que queremos filtrar, todo lo demás se difiere de nuevo al RecursiveRegexIterator. Tenga en cuenta que la clase es abstract ya que en realidad no es do nada útil: el filtrado real lo deben hacer las dos clases, que ampliarán este. Además, se puede llamar FilesystemRegexFilter, pero no hay nada que obligue (en este nivel) a filtrar las clases relacionadas con el sistema de archivos (hubiera elegido un nombre mejor, si no tuviera tanto sueño).

abstract class FilesystemRegexFilter extends RecursiveRegexIterator { 
    protected $regex; 
    public function __construct(RecursiveIterator $it, $regex) { 
     $this->regex = $regex; 
     parent::__construct($it, $regex); 
    } 
} 

Estas dos clases son filtros muy básicos, que actúan sobre el nombre del archivo y el nombre del directorio, respectivamente.

class FilenameFilter extends FilesystemRegexFilter { 
    // Filter files against the regex 
    public function accept() { 
     return (! $this->isFile() || preg_match($this->regex, $this->getFilename())); 
    } 
} 

class DirnameFilter extends FilesystemRegexFilter { 
    // Filter directories against the regex 
    public function accept() { 
     return (! $this->isDir() || preg_match($this->regex, $this->getFilename())); 
    } 
} 

para poner esos en práctica, los siguiente se repite recursivamente a través de los contenidos del directorio en el que reside el guión (no dude en modificar este!) Y filtra los .Trash carpetas (asegurándose de que los nombres de las carpetas hacer coincidir la expresión regular especialmente diseñada), y aceptar solo archivos PHP y HTML.

$directory = new RecursiveDirectoryIterator(__DIR__); 
// Filter out ".Trash*" folders 
$filter = new DirnameFilter($directory, '/^(?!\.Trash)/'); 
// Filter PHP/HTML files 
$filter = new FilenameFilter($filter, '/\.(?:php|html)$/'); 

foreach(new RecursiveIteratorIterator($filter) as $file) { 
    echo $file . PHP_EOL; 
} 

De particular interés es que, dado que nuestros filtros son recursivos, podemos optar por jugar con la forma de iterar sobre ellos. Por ejemplo, podríamos fácilmente nos limitamos a analizar únicamente hasta 2 niveles de profundidad (incluyendo la carpeta de partida) haciendo:

$files = new RecursiveIteratorIterator($filter); 
$files->setMaxDepth(1); // Two levels, the parameter is zero-based. 
foreach($files as $file) { 
    echo $file . PHP_EOL; 
} 

También es súper fácil de añadir aún más filtros (creando una instancia de más de nuestro filtrado clases con diferentes expresiones regulares, o creando nuevas clases de filtrado) para necesidades de filtrado más especializadas (por ejemplo, tamaño de archivo, longitud de ruta completa, etc.).

P.S. Hmm esta respuesta balbucea un poco; Traté de mantenerlo lo más conciso posible (incluso eliminando vastas franjas de super-balbuceo). Disculpas si el resultado neto deja la respuesta incoherente.

+0

Realmente aprecio el enfoque Menos rápido (y menos sucio) que demuestra exactamente lo que estoy buscando. Gracias. Aunque la rápida y sucia hicieron error con Fatal error: no detectada excepción 'UnexpectedValueException' con el mensaje 'RecursiveDirectoryIterator :: __ construct (/var/www/html/.Trash-0) – Chris

+1

El error es nada realmente mal con el código (bar no 'try' -ing lo suficientemente fuerte), la causa más probable son los permisos de la carpeta (o la falta de ella). Me alegra que estés contento con la mejor alternativa de todos modos. :) – salathe

+0

Muy bien, pero ¿cómo se obtiene un objeto SplFileInfo para cada archivo, en lugar de una ruta simple? –

8

Los documentos no son de mucha ayuda. Hay un problema usando una expresión regular para 'no coincide' aquí, pero vamos a ilustrar un ejemplo de trabajo en primer lugar:

<?php 
//we want to iterate a directory 
$Directory = new RecursiveDirectoryIterator("/var/dir"); 

//we need to iterate recursively 
$It  = new RecursiveIteratorIterator($Directory); 

//We want to stop decending in directories named '.Trash[0-9]+' 
$Regex1 = new RecursiveRegexIterator($It,'%([^0-9]|^)(?<!/.Trash-)[0-9]*$%'); 

//But, still continue on doing it **recursively** 
$It2  = new RecursiveIteratorIterator($Regex1); 

//Now, match files 
$Regex2 = new RegexIterator($It2,'/\.php$/i'); 
foreach($Regex2 as $v){ 
    echo $v."\n"; 
} 
?> 

El problema es la no coincide con .Trash[0-9]{3} parte: La única forma que conozco a negativo coincide con el directorio, es coincide con al final de la cadena $, y luego luego afirma con un lookbehind (?<!/foo) 'si no está precedido por'/foo '.

Sin embargo, como .Trash[0-9]{1,3} no es de longitud fija, no podemos usarlo como una aseveración lookbehind. Desafortunadamente, no hay 'coincidencia invertida' para un RegexIterator. Pero tal vez hay más gente con conocimientos de expresiones regulares entonces sabiendo cómo hacer coincidir 'cualquier cadena no termina con .Trash[0-9]+


edición: consiguió '%([^0-9]|^)(?<!/.Trash-)[0-9]*$%' como una expresión regular que hacer el truco.

+0

Apreciar la solución era simple y fácil de entender. – Chris

+0

$ it var no hace referencia –

1

Una mejora de salathe, sería olvidarse de la clase abstracta personalizada. sólo tiene que utilizar una buena programación orientada a objetos en PHP y se extienden directamente RecursiveRegexIterator lugar:

Aquí está el filtro de archivos

class FilenameFilter 
extends RecursiveRegexIterator 
{ 
    // Filter files against the regex 
    public function accept() 
    { 
     return ! $this->isFile() || parent::accept(); 
    } 
} 

Y el filtro de Directorio

class DirnameFilter 
extends RecursiveRegexIterator 
{ 
    // Filter directories against the regex 
    public function accept() { 
     return ! $this->isDir() || parent::accept(); 
    } 
} 
+0

Nota: este comportamiento es diferente a mi ejemplo. El suyo coincide con la expresión regular en contra del valor "actual" del iterador que se está filtrando (para 'Sistema de Archivos 'el valor" actual "se puede manipular usando indicadores). Mi ejemplo usa solo el nombre del archivo. – salathe