2010-07-23 36 views
22

Tôi vẫn chưa tìm thấy một ví dụ tốt về cách sử dụng trình RegexIterator php để đệ quy đi qua một thư mục.Làm thế nào để sử dụng RegexIterator trong PHP

Kết quả cuối cùng là tôi muốn chỉ định một thư mục và tìm tất cả các tệp trong đó bằng một số tiện ích nhất định. Nói ví dụ chỉ có phần mở rộng html/php. Hơn nữa, tôi muốn lọc ra các thư mục như vậy của các loại .Trash-0, .Trash-500, vv

<?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/>"; 
} 
?> 

Là những gì tôi có cho đến nay nhưng kết quả sau: Fatal error: của router 'UnexpectedValueException' với thông điệp 'RecursiveDirectoryIterator :: __ construct (/media/hdmovies1/.Trash-0)

Bất kỳ đề xuất nào?

Trả lời

46

Có một vài cách khác nhau để thực hiện điều gì đó như thế này, tôi sẽ đưa ra hai cách tiếp cận nhanh chóng để bạn lựa chọn: nhanh và bẩn, so với dài hơn và ít bẩn hơn (mặc dù, đó là đêm thứ sáu nên chúng tôi ' lại được phép đi một chút điên rồ).

1. Nhanh (và bẩn)

này liên quan đến chỉ viết một biểu thức chính quy (có thể được chia thành nhiều) sử dụng để lọc các bộ sưu tập các tập tin trong một lần mua sắm nhanh chóng.

(Chỉ có hai dòng nhận xét là thực sự quan trọng đối với khái niệm này.)

$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; 
} 

Cách tiếp cận này có một số vấn đề, mặc dù nó là nhanh chóng thực hiện được chỉ là một one-liner (mặc dù regex có thể là một nỗi đau để giải mã).

2. Ít nhanh chóng (và ít bẩn)

Một cách tiếp cận tái sử dụng được nhiều hơn là tạo ra một vài bộ lọc bespoke (sử dụng regex, hoặc bất cứ điều gì bạn muốn!) Để gọt xuống danh sách các sẵn các mục trong số RecursiveDirectoryIterator ban đầu chỉ dành cho những mục bạn muốn. Sau đây chỉ là một ví dụ, được viết nhanh cho bạn, mở rộng RecursiveRegexIterator.

Chúng tôi bắt đầu với một lớp cơ sở mà công việc chính của họ là giữ một regex mà chúng tôi muốn lọc, mọi thứ khác được hoãn lại cho RecursiveRegexIterator. Lưu ý rằng lớp là abstract vì nó không thực sự là làm bất kỳ điều gì hữu ích: việc lọc thực tế sẽ được thực hiện bởi hai lớp sẽ mở rộng lớp này. Ngoài ra, nó có thể được gọi là FilesystemRegexFilter nhưng không có gì buộc nó (ở cấp độ này) để lọc các lớp liên quan đến hệ thống tập tin (tôi đã chọn một tên tốt hơn, nếu tôi không khá buồn ngủ).

abstract class FilesystemRegexFilter extends RecursiveRegexIterator { 
    protected $regex; 
    public function __construct(RecursiveIterator $it, $regex) { 
     $this->regex = $regex; 
     parent::__construct($it, $regex); 
    } 
} 

Hai lớp này là bộ lọc rất cơ bản, hoạt động trên tên tệp và tên thư mục tương ứng.

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

Để đưa những thành thực tế, lặp sau đệ quy về nội dung của thư mục trong đó kịch bản cư trú (bạn có thể chỉnh sửa này!) Và lọc ra các .Trash thư mục (bằng cách đảm bảo rằng tên thư mục khớp với regex được tạo thủ công đặc biệt) và chỉ chấp nhận các tệp PHP và 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; 
} 

Lưu ý đặc biệt là do bộ lọc của chúng tôi đệ quy, nên chúng tôi có thể chọn cách sử dụng để lặp lại chúng. Ví dụ, chúng ta có thể dễ dàng hạn chế mình để chỉ quét lên đến 2 cấp độ sâu (bao gồm cả các thư mục khởi động) bằng cách thực hiện:

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

Đây cũng là siêu dễ dàng để thêm nhưng nhiều bộ lọc (bằng instantiating hơn về lọc của chúng tôi các lớp với các regex khác nhau, hoặc bằng cách tạo các lớp lọc mới) cho các nhu cầu lọc chuyên biệt hơn (ví dụ: kích thước tệp, độ dài đường dẫn đầy đủ, v.v.).

P.S. Hmm câu trả lời này một chút; Tôi đã cố gắng giữ cho nó ngắn gọn nhất có thể (thậm chí loại bỏ những vũng nước khổng lồ). Xin lỗi nếu kết quả ròng để lại câu trả lời không mạch lạc.

+0

Thực sự đánh giá cao cách tiếp cận ít nhanh hơn (và ít bẩn) nó chứng minh chính xác những gì tôi đang tìm kiếm. Cảm ơn. Mặc dù lỗi nhanh chóng và bị lỗi đã xảy ra với Lỗi nghiêm trọng: Ngoại lệ chưa được thực hiện 'UnexpectedValueException' với thông báo 'RecursiveDirectoryIterator :: __ construct (/var/www/html/.Trash-0) – Chris

+1

Lỗi là không có gì thực sự sai với mã (thanh không 'cố gắng'-ing đủ cứng), nguyên nhân có khả năng nhất là quyền của thư mục (hoặc thiếu nó). Vui vì bạn hài lòng với sự thay thế tốt hơn. :) – salathe

+0

Rất đẹp, nhưng làm thế nào để có được một đối tượng SplFileInfo cho mỗi tệp, chứ không phải là một đường dẫn đơn giản? –

8

Tài liệu thực sự không hữu ích nhiều. Có một vấn đề bằng cách sử dụng regex cho 'không phù hợp' ở đây, nhưng chúng tôi sẽ minh họa một ví dụ làm việc đầu tiên:

<?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"; 
} 
?> 

Vấn đề là không phù hợp với .Trash[0-9]{3} phần: Cách duy nhất tôi biết làm thế nào để tiêu cực phù hợp với thư mục, là khớp với kết thúc của chuỗi $, rồi sau đó xác nhận với một lookbehind (?<!/foo) 'nếu nó không đứng trước'/foo '.

Tuy nhiên, vì .Trash[0-9]{1,3} không phải là độ dài cố định, chúng tôi không thể sử dụng nó như một xác nhận lookbehind. Thật không may, không có 'đảo ngược trận đấu' cho một RegexIterator. Nhưng có lẽ có rất nhiều người regex am hiểu thì tôi biết làm thế nào để phù hợp với 'bất kỳ chuỗi không kết thúc với .Trash[0-9]+


chỉnh sửa: nhận nó '%([^0-9]|^)(?<!/.Trash-)[0-9]*$%' như một regex sẽ làm các trick.

+0

Đánh giá cao các giải pháp đó là đơn giản và dễ hiểu. – Chris

+0

$ Nó var được unreferenced –

1

Một cải tiến cho salathe, sẽ là để quên về lớp trừu tượng tùy chỉnh. Chỉ cần sử dụng OOP tốt trong PHP và trực tiếp mở rộng RecursiveRegexIterator thay vì:

Dưới đây là bộ lọc tập tin

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

Và bộ lọc thư mục

class DirnameFilter 
extends RecursiveRegexIterator 
{ 
    // Filter directories against the regex 
    public function accept() { 
     return ! $this->isDir() || parent::accept(); 
    } 
} 
+0

Lưu ý: hành vi này khác với ví dụ của tôi. Bạn khớp với regex so với bất kỳ giá trị "hiện tại" nào của trình lặp được lọc là (đối với 'FilesystemIterator' giá trị" hiện tại "có thể được sử dụng bằng cờ). Ví dụ của tôi chỉ sử dụng tên tệp. – salathe

Các vấn đề liên quan