Как использовать 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/>";
}
?>

То, в чем я имею до сих пор, но результат: Фатальная ошибка: Неперехваченное исключение 'UnexpectedValueException' с сообщением 'RecursiveDirectoryIterator:: __ конструкция (/media/hdmovies1/.Trash-0)

Какие-либо предложения?

22
задан Chris 17 November 2015 в 20:35
поделиться

2 ответа

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

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 . Обратите внимание, что этот класс является абстрактным , поскольку он на самом деле не делает ничего полезного: фактическая фильтрация должна выполняться двумя классами, которые расширят этот класс.Кроме того, он может называться 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()));
    }
}

Чтобы применить это на практике, следующее рекурсивно перебирает содержимое каталога, в котором находится сценарий (не стесняйтесь редактировать это!), И отфильтровывает папки .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;
}

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

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

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

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

49
ответ дан 29 November 2019 в 03:55
поделиться

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

<?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} часть: Единственный способ, которым я знаю, как отрицательно сопоставить каталог, - это сопоставить конец строки $ , а затем утверждать с просмотром назад (? ', если ему не предшествует' / foo '.

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


edit : понял '% ([^ 0-9] | ^) (? в качестве регулярного выражения поможет.

8
ответ дан 29 November 2019 в 03:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: