如何在PHP中使用RegexIterator

Chr*_*ris 22 php regex spl iterator

我还没有找到一个很好的例子,说明如何使用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/>";
}
?>
Run Code Online (Sandbox Code Playgroud)

到目前为止我得到的结果是:致命错误:带有消息'RecursiveDirectoryIterator :: __ construct(/media/hdmovies1/.Trash-0)的未捕获异常'UnexpectedValueException'

有什么建议?

sal*_*the 49

有几种不同的方式来做这样的事情,我会给你两种快速的方法供你选择:快速和肮脏,而不是更长和更少脏(虽然,这是一个星期五晚上所以我们被允许去有点疯狂).

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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法有很多问题,虽然很快就可以实现只是一个单行(尽管正则表达式可能很难解读).

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

特别值得注意的是,由于我们的过滤器是递归的,我们可以选择如何迭代它们.例如,我们可以轻松地将自己限制为仅扫描最多2级(包括起始文件夹):

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

添加更多过滤器(通过使用不同的正则表达式实例化更多我们的过滤类;或者通过创建新的过滤类)以获得更专业的过滤需求(例如文件大小,全路径长度等)也非常容易.

PS嗯这个答案有点唠叨; 我尽量保持简洁(甚至删除大量的超级喋喋不休).如果最终结果使答案不一致,请道歉.


Wri*_*ken 8

文档确实没什么用处.这里使用正则表达式"不匹配"存在问题,但我们将首先说明一个工作示例:

<?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";
}
?>
Run Code Online (Sandbox Code Playgroud)

问题是不匹配.Trash[0-9]{3}部分:我知道如何对目录进行否定匹配的唯一方法是匹配字符串的结尾$,然后使用lookbehind断言(?<!/foo)'如果它没有前面带'/ foo'.

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


编辑:得到它'%([^0-9]|^)(?<!/.Trash-)[0-9]*$%'作为正则表达式会做的伎俩.