27

我还没有找到一个很好的例子来说明如何使用 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::__construct(/media/hdmovies1/.Trash-0)

有什么建议么?

4

3 回答 3

55

有几种不同的方法可以处理这样的事情,我将提供两种快速方法供您选择:快速和肮脏,而不是更长和更少肮脏(不过,这是星期五晚上,所以我们可以去有点疯狂)。

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

为了将这些付诸实践,以下递归迭代脚本所在目录的内容(随意编辑它!)并过滤掉.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;
}

添加更多过滤器(通过使用不同的正则表达式实例化更多过滤类;或者通过创建新过滤类)以满足更专业的过滤需求(例如文件大小、完整路径长度等)也非常容易。

PS 嗯,这个答案有点啰嗦;我试图让它尽可能简洁(甚至删除大量的超级废话)。如果最终结果使答案不连贯,请道歉。

于 2010-07-23T21:47:19.557 回答
9

文档确实没有多大帮助。在这里使用正则表达式来表示“不匹配”存在问题,但我们将首先说明一个工作示例:

<?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)'如果它前面没有'/foo'的情况下进行断言。

但是,由于.Trash[0-9]{1,3}不是固定长度,我们不能将其用作后向断言。不幸的是,RegexIterator 没有“反转匹配”。但也许有更多精通正则表达式的人,然后我知道如何匹配'任何不以结尾的字符串.Trash[0-9]+


编辑:得到它'%([^0-9]|^)(?<!/.Trash-)[0-9]*$%',因为正则表达式可以解决问题。

于 2010-07-23T20:00:10.253 回答
1

对salathe 的改进是忘记自定义抽象类。只需在 PHP 中使用良好的 OOP 并直接扩展 RecursiveRegexIterator:

这是文件过滤器

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

和目录过滤器

class DirnameFilter 
extends RecursiveRegexIterator 
{
    // Filter directories against the regex
    public function accept() {
        return ! $this->isDir() || parent::accept();
    }
}
于 2013-01-08T16:40:13.860 回答