2010-07-23 5 views
22

PHP RegexIterator를 사용하여 재귀 적으로 디렉토리를 트래버스하는 좋은 예를 아직 찾지 못했습니다.PHP에서 RegexIterator를 사용하는 방법

최종 결과는 디렉토리를 지정하고 지정된 확장자를 사용하여 디렉토리를 모두 찾으려는 것입니다. 예를 들어 html/php 확장 만 말하십시오. 또한, 나는 내가 지금까지 가지고 있지만 초래할 것 등

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

유형 .Trash-0, .Trash-500의 폴더를 필터링 할 수 있나요합니다 : 치명적인 오류 : 메시지 catch되지 않은 예외 'UnexpectedValueException를' 'RecursiveDirectoryIterator :: __ construct (/media/hdmovies1/.Trash-0)

어떤 제안이 있습니까?

답변

46

이런 식으로 갈 여러 가지 방법이 있습니다. 두 가지 빠른 방법을 선택할 수 있습니다 : 빠르고 더러운 대 더 길고 덜 더러운 것 (하지만 금요일 밤입니다. 조금 더 미친 듯이 허락 받는다).

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이라고 불릴 수도 있지만 파일 시스템과 관련된 클래스를 필터링하도록 (이 수준에서) 강제로 적용되는 것도 없습니다. (나는 아주 졸린 것이 아니라면 더 나은 이름을 선택했을 것입니다.)

이 두 클래스는 파일 이름과 디렉토리 이름 각각에 대해 매우 기본적인 필터입니다.

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

우리의 필터링을 더 인스턴스화하여 (아직 이상의 필터를 추가하는 것이 슈퍼 쉽다 (예 : 파일 크기, 전체 경로 길이 등)에 대한 새로운 필터링 클래스를 생성하여 새로운 정규식을 가진 클래스를 만들 수 있습니다.

P. 흠,이 대답은 조금 엉망이됩니다. 나는 가능한 한 간결하게 유지하려고 노력했다. (심지어 광대 한 광대 한 부분을 제거하는 것조차). 그물 결과가 응답을 일관성 없게 남겨두면 사과하십시오.

+0

내가 찾고있는 것을 정확히 보여주는 덜 빠르기 (그리고 덜 부정한) 접근법을 정말 고맙게 생각합니다. 감사. 신속하고 더러운하지만 이 치명적인 오류와 에러 출력했다 : RecursiveDirectoryIterator :: __ 구조 (/var/www/html/.Trash-0) '메시지가 catch되지 않은 예외'UnexpectedValueException을 ' – Chris

+1

오류는 정말 아무 문제 없습니다 코드 (바 열심히 시도하지 말 것), 가장 큰 원인은 폴더의 사용 권한 (또는 부족)입니다. 어쨌든 당신이 더 나은 대안에 만족한다면 다행입니다. :) – salathe

+0

아주 좋지만, 간단한 경로가 아닌 각 파일에 대해 SplFileInfo 개체를 얻는 방법은 무엇입니까? –

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

문제는이 .Trash[0-9]{3} 일부가 일치하지 않는 것입니다 이 일치하면 문자열의 끝이 $이고 '/ foo'가 선행하지 않으면 lookbehind가 (?<!/foo) '으로 어설 션됩니다.

그러나 .Trash[0-9]{1,3}은 고정 길이가 아니기 때문에 lookbehind 어설 션으로 사용할 수 없습니다. 불행히도 RegexIterator에는 '반전 일치'가 없습니다. 그러나 아마 나는 어떤 문자열이 .Trash[0-9]+


편집로 끝나는하지 '과 일치하는 방법을 아는 더 정규식에 정통한 사람들이있다 : 그것은 트릭을 할 것 정규식으로 '%([^0-9]|^)(?<!/.Trash-)[0-9]*$%'을 얻었다.

+0

간단하고 이해하기 쉬웠다 솔루션을 감사합니다입니다. – Chris

+0

$ It var는 참조되지 않습니다 –

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

참고 :이 동작은 제 예제와 다릅니다. 필터 된 반복자의 "현재"값이 무엇이든간에 정규 표현식과 일치합니다 ('FilesystemIterator'의 경우 "현재"값은 플래그를 사용하여 조작 할 수 있습니다). 내 예제에서는 파일 이름 만 사용합니다. – salathe

관련 문제