Computer >> 컴퓨터 >  >> 프로그래밍 >> PHP

PHP RegexIterator 활용법: 정규표현식으로 파일·디렉터리 필터링하기

PHP의 RegexIterator는 반복자(iterator)를 순회하면서 각 요소를 정규표현식과 비교하고, 패턴에 일치하는 항목만 통과시켜 주는 SPL 클래스입니다. 이를 RecursiveDirectoryIterator와 조합하면 하위 폴더까지 자동으로 탐색하면서 원하는 파일만 손쉽게 추출할 수 있습니다.

1. 정규표현식으로 바로 필터링하기

가장 간단한 방법은 재귀 반복자 위에 RegexIterator를 얹는 것입니다. 아래 예제는 현재 디렉터리를 재귀적으로 탐색하면서, .Trash로 시작하는 휴지통 폴더는 건너뛰고 확장자가 .php 또는 .html인 파일만 출력합니다.

$directory = new RecursiveDirectoryIterator(__DIR__);
$flattened = new RecursiveIteratorIterator($directory);

// "/.Trash*" 폴더는 제외하고, 경로가 .php 또는 .html 파일로 끝나야 함
$files = new RegexIterator($flattened, '#^(?:[A-Z]:)?(?:/(?!\.Trash)[^/]+)+/[^/]+\.(?:php|html)$#Di');
foreach($files as $file) {
    echo $file . PHP_EOL;
}

패턴 끝의 D 플래그는 Windows 드라이브 문자(예: C:)도 처리할 수 있도록 해주고, i 플래그는 대소문자를 구분하지 않게 만들어 줍니다.

2. 커스텀 필터 클래스 만들기

조건이 복잡해지면 코드를 여러 개의 재사용 가능한 필터 클래스로 분리하는 것이 좋습니다. 먼저 정규표현식을 보관하는 추상 기반 클래스를 정의하고, 이를 RecursiveRegexIterator로 확장합니다.

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

이 기반 클래스를 상속받아, 하나는 파일 이름, 다른 하나는 디렉터리 이름을 검사하는 두 개의 기본 필터를 만듭니다. accept() 메서드가 true를 반환하는 요소만 순회 대상에 포함됩니다.

class FilenameFilter extends FilesystemRegexFilter {
    // 정규표현식으로 파일 필터링
    public function accept() {
        return ( ! $this->isFile() || preg_match($this->regex, $this->getFilename()));
    }
}
class DirnameFilter extends FilesystemRegexFilter {
    // 정규표현식으로 디렉터리 필터링
    public function accept() {
        return ( ! $this->isDir() || preg_match($this->regex, $this->getFilename()));
    }
}

3. 필터 체이닝으로 디렉터리 순회하기

필터는 반복자를 감싸는 형태이므로, 여러 개를 겹쳐서(체이닝) 사용할 수 있습니다. 아래 코드는 디렉터리 내용을 재귀적으로 순회하면서 휴지통(.Trash*) 폴더를 먼저 걸러내고, 그다음 PHP와 HTML 파일만 남깁니다.

$directory = new RecursiveDirectoryIterator(__DIR__);

// ".Trash*" 폴더 제외
$filter = new DirnameFilter($directory, '/^(?!\.Trash)/');

// PHP/HTML 파일만 허용
$filter = new FilenameFilter($filter, '/\.(?:php|html)$/');

foreach(new RecursiveIteratorIterator($filter) as $file) {
    echo $file . PHP_EOL;
}

이 방식은 정규표현식 한 줄로 처리하는 첫 번째 예제보다 가독성이 좋고, 나중에 새로운 조건이 필요할 때 해당 부분만 수정하면 되므로 유지보수에도 유리합니다.

탐색 깊이 제한하기

같은 필터를 재사용하면서 최대 2단계 깊이까지만 탐색하고 싶다면 setMaxDepth() 메서드를 사용하면 됩니다. 매개변수는 0부터 시작하므로 1을 지정하면 총 두 단계까지 탐색됩니다.

$files = new RecursiveIteratorIterator($filter);
$files->setMaxDepth(1); // 두 단계 탐색 (매개변수는 0-based)
foreach($files as $file) {
    echo $file . PHP_EOL;
}

마무리

RegexIterator와 커스텀 필터 클래스를 활용하면 복잡한 파일 시스템 탐색 로직도 선언적이고 깔끔하게 작성할 수 있습니다. 대량의 파일을 다루는 백업 스크립트, 소스 코드 스캐너, 정적 사이트 생성기 등을 만들 때 이 패턴을 적극적으로 활용해 보세요.