如何在PHP中使用RegexIterator
我还没有find一个很好的例子,如何使用PHP的RegexIteratorrecursion遍历目录。
最终的结果将是我想指定一个目录,并find一些给定的扩展名中的所有文件。 例如只说html / php扩展。 此外,我想过滤出types为.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/>"; } ?>
是我到目前为止,但导致:致命错误:未收到的exception'UnexpectedValueException'消息'RecursiveDirectoryIterator :: __构造(/media/hdmovies1/.Trash-0)
有什么build议么?
有几种不同的方式来做这样的事情,我会给你两个快速的方法供你select:快速和肮脏的,而不是更长,更肮脏(但是,这是一个星期五晚上,所以我们被允许去有点疯狂)。
1.快速(而且脏)
这涉及到只需编写一个正则expression式(可以拆分成多个)来使用一次快速筛选文件集合。
(只有两个注释行对这个概念非常重要。)
$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, '#^(?:[AZ]:)?(?:/(?!\.Trash)[^/]+)+/[^/]+\.(?:php|html)$#Di'); foreach($files as $file) { echo $file . PHP_EOL; }
这种方法有许多问题,尽pipe实现只是一个单一的内容(尽pipe正则expression式可能是一个破译的痛苦)。
2.less快(而且less脏)
一个更可重用的方法是创build一个定制的filter(使用正则expression式,或任何你喜欢的!)来减less在初始RecursiveDirectoryIterator
中的可用项目列表,只有那些你想要的。 以下仅仅是一个例子,只是为了你的扩展RecursiveRegexIterator
。
我们从一个基类开始,它的主要工作是保持我们要过滤的正则expression式,其他所有东西都被推迟到RecursiveRegexIterator
。 请注意,该类是abstract
因为它实际上并没有做任何有用的事情:实际的过滤是由两个类来完成的,这两个类将扩展这个类。 此外,它可能被称为FilesystemRegexFilter
但没有什么强迫它(在这个级别)来过滤文件系统相关的类(如果我不那么昏昏欲睡,我会select一个更好的名字)。
abstract class FilesystemRegexFilter extends RecursiveRegexIterator { protected $regex; public function __construct(RecursiveIterator $it, $regex) { $this->regex = $regex; parent::__construct($it, $regex); } }
这两个类是非常基本的filter,分别作用于文件名和目录名。
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())); } }
为了实践这些,下面的代码recursion地遍历脚本所在目录的内容(随意编辑!)并过滤出.Trash
文件夹(通过确保文件夹名称与特制的正则expression式匹配 ) ,只接受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; }
特别值得注意的是,由于我们的滤波器是recursion的,所以我们可以select如何遍历它们。 例如,我们可以很容易地将自己限制为仅扫描最多2层(包括起始文件夹),方法是:
$files = new RecursiveIteratorIterator($filter); $files->setMaxDepth(1); // Two levels, the parameter is zero-based. foreach($files as $file) { echo $file . PHP_EOL; }
对于更多专门的过滤需求(例如文件大小,全path长度等),添加更多的filter(通过实例化更多不同正则expression式的过滤类;或创build新的过滤类)也是非常容易的。
PS嗯这个答案唠叨了一下; 我试图尽可能保持简洁(甚至去除大片的超级喋喋不休)。 如果最终的结果让答案不连贯,我们抱歉。
文档确实没有太大的帮助。 在这里使用正则expression式的'不匹配'有一个问题,但我们将首先举例说明一个工作示例:
<?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}
部分:我知道如何去反向匹配目录的唯一方法是匹配string$
的末尾,然后用lookbehind (?<!/foo)
“,如果它没有在”/ foo“之前。
但是,由于.Trash[0-9]{1,3}
不是固定长度,我们不能用它作为后向断言。 不幸的是,RegexIterator没有“反向匹配”。 但也许有更多的正则expression式的人,然后我知道如何匹配任何string不以.Trash[0-9]+
编辑 :得到它作为一个正则expression式'%([^0-9]|^)(?<!/.Trash-)[0-9]*$%'
将做的伎俩。
对salathe的改进,将是忘记自定义的抽象类。 只需在PHP中使用良好的OOP,直接扩展RecursiveRegexIterator即可:
这是文件filter
class FilenameFilter extends RecursiveRegexIterator { // Filter files against the regex public function accept() { return ! $this->isFile() || parent::accept(); } }
和目录filter
class DirnameFilter extends RecursiveRegexIterator { // Filter directories against the regex public function accept() { return ! $this->isDir() || parent::accept(); } }