profile

이 세상에 하나는 남기고 가자

세상에 필요한 소스코드 한줄 남기고 가자

PHP 정규식(PCRE)의 모든 것 - 서브 패턴(Subpatterns)

유영재

Subpatterns (서브 패턴)

서브 패턴은 둥근 괄호(())로 구분되며 중첩 될 수 있다. 패턴의 일부를 서브 패턴으로 표시하는 것은 다음 두 가지를 수행한다.

  1. 선택 집합을 지역화 한다. 예를 들어 cat(aract|erpillar|) 패턴은 "cat", "cataract" 또는 "caterpillar" 중 하나와 일치한다. 괄호가 없으면 "cataract", "erpillar" 또는 빈 문자열과 일치한다.

  2. 서브 패턴을 캡처 서브 패턴으로 설정한다. 모든 패턴이 일치하면 서브 패턴과 일치하는 문자열의 부분은 pcre_exec()ovector 인수를 통해 호출자에게 다시 전달된다. 여는 괄호는 왼쪽에서 오른쪽으로 (1부터 시작) 카운트하여 캡처 서브 패턴의 수를 부여한다.

예를 들어 문자열 "the red king"이 ((red|white) (king|queen)) 패턴과 일치하면 캡처된 부분 문자열이 "red king", "red" 및 "king"이며 아래와 같이 번호가 부여된다.

<?php
$string = 'the red king';
preg_match('/((red|white) (king|queen))/', $string, $matches);
print_r($matches);
//Array
//(
//  [0] => red king
//  [1] => red king
//  [2] => red
//  [3] => king
//)

평범한 괄호가 두 가지 기능을 수행한다는 사실이 항상 도움이 되는 것은 아니다. 캡처 요구 사항없이 그룹화 서브 패턴이 필요한 경우가 종종 있다. 여는 괄호 뒤에 "?:"가 오는 경우 서브 패턴은 캡처를 수행하지 않으며 후속 캡처 서브 패턴의 수를 계산할 때는 계산되지 않는다.

예를 들어 문자열 "the white queen"이 ((?:red|white) (king|queen)) 패턴과 일치하면 캡처 된 부분 문자열은 "white queen"과 "queen"이며 1과 2로 번호가 매겨진다. 캡처된 부분 문자열의 최대 수는 99이며 캡처 및 캡처하지 않는 모든 서브 패턴의 최대 수는 200개다.

<?php
$string = 'the white queen';
preg_match('/((?:red|white) (king|queen))/', $string, $matches);
print_r($matches);
//Array
//(
//  [0] => white queen
//  [1] => white queen
//  [2] => queen
//)

캡처하지 않는 서브 패턴의 옵션이 필요한 경우 아래의 두 예시 처럼 "?" 그리고 ":" 사이에 옵션을 표기할 수 있다.

(?i:saturday|sunday)
(?:(?i)saturday|sunday)

정확히 동일한 문자열 세트와 일치해야 한다. 대체 분기는 왼쪽에서 오른쪽으로 시도되고 서브 패턴의 끝에 도달 할 때까지 옵션이 재설정 되지 않아 한 분기의 옵션 설정이 후속 분기에 영향을 미치므로 위 패턴은 "SUNDAY"및 "Saturday"와 일치한다.

<?php
preg_match('/(?i:saturday|sunday)/','Saturday', $matches);
print_r($matches);
// Array
// (
//     [0] => Saturday
// )

preg_match('/(?:(?i)saturday|sunday)/','SUNDAY', $matches);
print_r($matches);
// Array
// (
//     [0] => SUNDAY
// )

구문 (?P<name>pattern)을 사용하여 서브 패턴의 이름을 지정할 수 있다. 그런 다음이 서브 패턴은 matches 배열에서 일반 숫자 위치와 이름을 기준으로 인덱싱 된다(PHP 5.2.2에서는 두 가지 대체 구문 (?<name>pattern)(?'name'pattern)을 사용할 수 있다).

<?php
$string = 'the white queen';
preg_match('/(?P<name>the)/', $string, $matches);
print_r($matches);
preg_match("/(?'name'the)/", $string, $matches);
print_r($matches);
//Array
//(
//  [0] => the
//  [name] => the
//  [1] => the
//)
//Array
//(
//  [0] => the
//  [name] => the
//  [1] => the
//)

때로는 일치하는 여러 개의 서브 그룹을 정규식에 포함시켜야 할 필요가 있다. 일반적으로 이들 중 하나만 일치할 가능성이 있더라도 각각의 고유한 역 참조 번호가 부여된다. 이 문제를 극복하기 위해 (?| 구문을 사용하면 중복되는 숫자를 가질 수 있다. 다음 정규식 (?:(Sat)ur|(Sun))day을 "Sunday" 문자열과 비교해보자.

<?php
$string = 'Sunday';
preg_match('/(?:(Sat)ur|(Sun))day/', $string, $matches);
print_r($matches);
//Array
//(
//  [0] => Sunday
//  [1] =>
//  [2] => Sun
//)

여기에서 "Sun"은 역 참조 2에 저장되고 역 참조 1은 비어 있다. 역 참조 1에서의 "Sat"는 역 참조 2에서 존재하지 않는다. (?|를 사용하여 패턴을 변경하면 이 문제가 해결된다.

<?php
$string = 'Sunday';
preg_match('/(?|(Sat)ur|(Sun))day/', $string, $matches);
print_r($matches);
//Array
//(
//  [0] => Sunday
//  [1] => Sun
//)

이 패턴을 사용하면 Sun과 Sat가 모두 역 참조 1에 저장된다.


comments powered by Disqus