Подтвердить что ты не робот

Как использовать RegexIterator в PHP

Мне еще предстоит найти хороший пример того, как использовать php RegexIterator для рекурсивного прохождения каталога.

Конечным результатом будет то, что я хочу указать каталог и найти все файлы в нем с некоторыми заданными расширениями. Скажем, например, только расширения html/php. Кроме того, я хочу отфильтровать папки такого типа .Trash-0,.Trash-500 и т.д.

<?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/>";
}
?>

Это то, что у меня есть до сих пор, но результат: Fatal error: Uncaught exception 'UnexpectedValueException' с сообщением 'RecursiveDirectoryIterator:: __ construct (/media/hdmovies1/.Trash-0)

Любые предложения?

4b9b3361

Ответ 1

Есть несколько разных способов сделать что-то вроде этого, я дам вам два быстрых подхода: быстро и грязно, по сравнению с дольше и менее грязными (хотя это пятница, так что мы позволили немного сходить с ума).

1. Быстрый (и грязный)

Это включает в себя просто запись регулярного выражения (может быть разделено на несколько) для использования для фильтрации коллекции файлов одним быстрым махом.

(Только две прокомментированные строки действительно важны для концепции.)

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

В этом подходе есть ряд проблем, хотя быстро реализовать это просто однострочный (хотя регулярное выражение может быть болью для расшифровки).

2. Менее быстрые (и менее грязные)

Более многоразовый подход заключается в создании нескольких настраиваемых фильтров (с использованием регулярных выражений или любого другого, что вам нравится!), чтобы уничтожить список доступных элементов в начальном RecursiveDirectoryIterator до тех пор, пока вы не захотите. Ниже приведен только один пример, написанный быстро для вас, расширения RecursiveRegexIterator.

Начнем с базового класса, основной задачей которого является удерживание регулярного выражения, которое мы хотим отфильтровать, все остальное отложено обратно на RecursiveRegexIterator. Обратите внимание, что класс abstract, поскольку он фактически не делает ничего полезного: фактическая фильтрация должна выполняться двумя классами, которые будут расширять этот. Кроме того, его можно назвать FilesystemRegexFilter, но нет ничего, что вынуждало бы (на этом уровне) фильтровать классы, связанные с файловой системой (я бы выбрал лучшее имя, если бы я не был настолько сонным).

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

Эти два класса - это очень простые фильтры, действующие соответственно на имя файла и имя каталога.

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

Чтобы реализовать их на практике, следующие повторяются по каталогу, в котором находится script (не стесняйтесь редактировать это!) и отфильтровывайте папки .Trash (убедившись, что имена папок совпадают с специально созданным регулярным выражением) и принимают только файлы PHP и 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;
}
Особо следует отметить, что, поскольку наши фильтры рекурсивные, мы можем поиграть с тем, как их перебирать. Например, мы могли бы легко ограничиться только сканированием до 2 уровней (включая начальную папку), выполнив следующие действия:
$files = new RecursiveIteratorIterator($filter);
$files->setMaxDepth(1); // Two levels, the parameter is zero-based.
foreach($files as $file) {
    echo $file . PHP_EOL;
}

Также очень легко добавлять еще больше фильтров (путем создания дополнительных классов фильтрации с различными регулярными выражениями или путем создания новых классов фильтрации) для более специализированных задач фильтрации (например, размер файла, длина полного пути и т.д.)..).

P.S. Хмм, этот ответ немного болтает; Я старался держать его как можно более кратким (даже удаляя огромные кусочки супер-лепет). Извините, если чистый результат не дает ответа некогерентным.

Ответ 2

Документы действительно не очень полезны. Там проблема с использованием регулярного выражения для "не соответствует" здесь, но сначала проиллюстрируем рабочий пример:

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

Проблема заключается в том, что не соответствует части .Trash[0-9]{3}: единственный способ, которым я знаю, как отрицательное соответствие каталогу, соответствует конец строки $, а затем утверждать с lookbehind (?<!/foo) ', если ему не предшествует'/foo '.

Однако, поскольку .Trash[0-9]{1,3} не является фиксированной длиной, мы не можем использовать его как утверждение lookbehind. К сожалению, нет никакого инвертированного соответствия для RegexIterator. Но, возможно, есть более опытные люди с регулярными выражениями, тогда я знаю, как сопоставить "любую строку, не заканчивающуюся на .Trash[0-9]+


edit: получил '%([^0-9]|^)(?<!/.Trash-)[0-9]*$%', поскольку регулярное выражение выполнит трюк.

Ответ 3

Улучшение salathe, было бы забыть о пользовательском абстрактном классе. Просто используйте хороший ООП в PHP и вместо этого прямо создайте рекурсивныйRegexIterator:

Вот фильтр файлов

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

И фильтр Directory

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