如何在PHP中使用RegexIterator

如何在PHP中使用RegexIterator,php,regex,iterator,spl,Php,Regex,Iterator,Spl,我还没有找到一个很好的例子来说明如何使用php正则表达式递归遍历目录 最终的结果是,我想指定一个目录,并查找其中具有某些给定扩展名的所有文件。例如,只说html/php扩展。此外,我想过滤掉.Trash-0、.Trash-500等类型的文件夹 <?php $Directory = new RecursiveDirectoryIterator("/var/www/dev/"); $It = new RecursiveIteratorIterator($Directory); $Regex

我还没有找到一个很好的例子来说明如何使用php正则表达式递归遍历目录

最终的结果是,我想指定一个目录,并查找其中具有某些给定扩展名的所有文件。例如,只说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::u构造(/media/hdmovies1/.Trash-0)


有什么建议吗

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

<?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}
部分:我知道如何对目录进行负匹配的唯一方法是匹配字符串
$
的结尾,然后用lookback
断言(?'如果它前面没有“/foo”)

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



编辑:明白了,
“%([^0-9]|^)(?有两种不同的方法来处理类似的事情,我将提供两种快速方法供您选择:快速和肮脏,而不是更长和更少肮脏(不过,这是一个周五晚上,所以我们可以有点疯狂)

1.快(脏)

这涉及到只编写一个正则表达式(可以拆分为多个)以用于快速筛选文件集合

(只有两行注释对概念来说才是真正重要的。)

这种方法有很多问题,尽管它很快就实现为一行程序(尽管正则表达式可能很难破译)

2.不那么快(也不那么脏)

一个更可重用的方法是创建两个定制的过滤器(使用regex或任何您喜欢的工具!)将初始的
RecursiveDirectoryIterator
中的可用项列表缩减为您想要的项。以下仅是一个扩展
RecursiveEGExitrator
的示例,只为您快速编写

我们从一个基类开始,基类的主要任务是保留我们想要过滤的正则表达式,其他的一切都被推迟到
递归EGEXITERATOR
。请注意,这个类是
抽象的,因为它实际上没有做任何有用的事情:实际的过滤是由两个类完成的,这两个类将扩展它另外,它可能被称为
FilesystemRegexFilter
,但没有什么强迫它(在这个级别上)过滤与文件系统相关的类(如果我不是很困的话,我会选择一个更好的名称)

这两个类是非常基本的过滤器,分别作用于文件名和目录名

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


顺便说一句,这个答案有点含糊不清;我尽量保持它的简洁(甚至删除了大量的超级含糊不清)。如果最终结果导致答案不连贯,我深表歉意。

Salath的一个改进是,忘记自定义抽象类。 只需在PHP中使用良好的OOP,直接扩展RecursiveGenereXiterator即可:

这是文件过滤器

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

欣赏这个简单易懂的解决方案。$it var是未引用的。真正欣赏不那么快(也不那么脏)的解决方案方法它正好演示了我要查找的内容。谢谢。尽管快速而肮脏的方法确实出现了致命错误:未捕获异常“UnexpectedValueException”,消息为“RecursiveDirectoryIterator::\uu构造(/var/www/html/.Trash-0),但该错误与代码没有任何关系(请不要
尝试
-ing),最有可能的原因是文件夹的权限(或缺少权限)。很高兴您对更好的选择感到满意。:)很好,但是如何为每个文件获得一个SplFileInfo对象,而不是一个简单的路径?@mr.w
$file
在上面的示例中是一个
SplFileInfo
对象。“越不快越不脏”解决方案很有魅力!谢谢!:)注意:这种行为与我的示例不同。您的将正则表达式与被筛选的迭代器的“当前”值匹配(对于
filesystemerator
可以使用标志操纵“当前”值)。我的示例仅使用文件名。
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();
    }
}