티스토리 뷰

테그 사이 내용을 확인하여 만족하는 경우에만 결과로 잡기

<b></b> 테그 사이의 내용에 <img> 테그가 없는 문자열만 찾고 싶을 경우, (?(?=)) 형태의 IF 문을 사용해서 정규식을 짤 수 있습니다.

형식:
(?(?=pattern)true pattern|false pattern)
예:
abcdefghijklmn
(?(?=abcd)abcd|efgh)


위 예는 간단히 설명하기 위한 것이고 실제 저런 경우 사용되지는 않습니다.
IF 형식의 문법으로 식이 맞을 경우 참에 해당하는 패턴을, 맞지 않을 경우 거짓에 해당하는 패턴을 실행하게 됩니다. 이 외에도 (?!pattern) , (?<=pattern) , (?<!pattern) 이 있습니다.
이에 대한 자세한 설명은 이곳을 참고하세요.

$string = <<<___HTML___
test<b><img src="asdflasf" width="100" /></b> end
test <b>TEST</b> end
test <b> adkj </b> end
___HTML___;

preg_match_all(
    '!<b>
        (?(?=\s*<img\s+)  # IF: 만약 <img 테그가
        |                 # 있을경우: 아무것도 검출하지 않음
        .*?               # 없을경우: <b></b>안의 내용을 검출
    )</b>!xis'
    $string,
    $match
);

echo '<pre>'. print_r($match, true) .'</pre>';



정규식을 보면 조건절이 있고, 참일 경우 아무런 값이 없고, 거짓일 경우 .*? 식이 있습니다.
즉, <img 테그가 들어간 문자열로 시작될 경우, 아무것도 검출하지 말고, 거짓일 경우(.*?)로 <b></b> 테그 안의 내용을 검출하는 정규식입니다.

IF 문이 쓰이는 상황은 극히 드뭅니다. 이 문법 없이도 정규식이 가능한 경우가 많기 때문입니다. 사용은 그리 어렵지 않지만 정규식을 다시 분석하는데에는 약간 어려움이 있게 되므로 무조건 쓰기보다는 가급적 안쓰고 사용하는게 좋고, 사용될 경우 x 옵션을 준 뒤에 코멘트를 자세히 달아주는 것이 좋습니다. (위 코드 참고)



HTML 문서에서 속성이 있는 테그만 캡쳐

이 정규식은 말 그대로 속성이 있는 테그만 캡쳐합니다.
이 경우 제일 간단한 식은 <[a-z]+\s+.*?> 이지만, 이 경우 쌍따옴표나 홀따옴표 안에 > 가 포함된 경우 잘못된 결과가 나오게 됩니다. 이런 문제를 해결하기 위해서는 각 따옴표가 있는 경우와 없는 경우 모두 비교해서 검출하면 됩니다.

아래 정규식은 테그와 속성명, 그리고 속성값이 있는 경우만 검출됩니다.

$content = <<<__HTML__
<P align=left>테스트 문자열<BR>
<A href="http://kai.ibbun.net/" title="따옴표 안에 > \"따옴표\"넣기" target="_blank">
<IMG alt=http://kai.ibbun.net/ title=" <<'따옴표' 안에 테그를
넣어도 정확히 검출됨>> " src="http://kai.ibbun.net/codesamples/image/a_dreamy_world.jpg"></A></p>
<div style='clear:both;' lb_exif='Y' onClick='testFunc(\'<TEST>\');'>테스트 문자열</div>
__HTML__;

preg_match_all(
    '@<([a-zA-Z]+)\s+                   # 영문으로 시작하는 테그명
        (?:[\w]+[\s=]+                  # 영문,숫자,언더바(_)로 시작하는 속성명
            (?:"(?(?=\\\\")\\\\"|.*?)*?"   # 쌍따옴표(")
            |\'(?(?=\\\\\')\\\\\'|.*?)*?\' # 홀따옴표(\')
            |[^\s<>]+)                     # 따옴표가 없는 경우, 공백 또는 <>까지
        \s*)*?                          # 전체 속성을 검출하기 위해 ( )를 두개씀
    /?>@xs',
    $content,
    $match
);
echo '<pre>'. print_r($match, true) .'</pre>';


이 정규식은 말그대로 속성이 있는 테그들만 캡쳐합니다. 캡쳐된 내용을 보면 아시겠지만, 테그의 전체 내용하고 테그명, 그리고 전체 속성 내용이 캡쳐 됩니다.

속성을 따로 구하려고 한다면 아래에 소개할 preg_replace_callback() 함수를 사용하거나, 캡쳐된 정규식을 가지고 다시 반복문을 돌려서 속성을 구해야 합니다.
위의 정규식을 조금 변형한다면 그리 어렵지 않게 구할 수 있습니다.


테그 제거시 특정 테그의 내용까지 제거

strip_tags() 를 하게되면 <script></script> 나 <textarea></textarea> 등의 내용이 남게 됩니다. 그래서 글 내용과 혼동이 생기게 됩니다.
이런 문제를 해결하기 위해서, 특정 테그를 우선적으로 검사하고, 그 뒤에 모든 테그를 삭제합니다.

$content = <<<___HTML___
<script type="text/javascript" src="test.js"></script>
test content 1<br />
<div style="background:#AFAFAF;">test content 2</div>
<textarea>TextArea test content.</textarea><br />
test content 3<br />
<strong>test content 4</strong>
<script><!--
alert('test');
//--></script>
___HTML___;
 
$content = preg_replace(
        '!<(script|javascript|jscript|style|textarea).*?</\1>|</?[\w]+.*?>!si',
        '',
        $content
);
echo '<pre>'. $content .'</pre>';


위 정규식에서 <(script).*?</\1> 부분의 \1 의 경우 정규식에서 ( )로 매칭된 서브패턴(Sub Pattern)을 역참조(Back Reference)를 하는 것입니다.

서브패턴으로 매칭되는 경우 순서대로 앞에서부터 번호가 붙으며, (?: )처럼 괄호 안의 내용을 검출하되 검출된 내용을 결과로 받지 않는 경우는 사용할 수 없습니다.


preg_replace_callback() 활용

이 함수를 간략히 설명 드리면, 패턴에 맞는 문자열을 찾았을 경우 자동으로 지정한 사용자 함수를 호출하고 매칭된 값을 인자로 넘겨주고, 사용자 함수에서 처리하여 결과 값을 돌려주면(return) 매칭된 부분을 교체(replace)하는 함수 입니다.
 
아래 예제는 문자열 중에서 소문자 test 를 찾고 <b></b> 테그를 추가하는 것입니다.

$string = <<<___HTML___
test content. it\'s TEST content test string
___HTML___;
$result = preg_replace_callback(
    '!(test)!',
    'test_callback_func',
    $string
);

// 사용자 콜백 함수
function test_callback_func($match) {
    return '<b>'. $match[1] .'</b>';
}

echo '<pre>'. htmlspecialchars(print_r($result, true)) .'</pre>';


preg_replace() 함수의 경우 '패턴', '변경 문자열', '입력값' 순으로 인자가 들어가지만, preg_replace_callback() 의 경우 '함수명'이 들어갑니다. 함수명은 괄호를 제외한 이름을 문자열로 적어야 합니다.

콜백함수에서는 일반 정규식에서 처럼 매칭된 배열이 인자값으로 들어오는데, 이 값은 preg_match() 나 preg_match_all() 에서 매칭된 값과 동일합니다.

콜백함수의 경우 어떨때는 매우 편리하지만 장단점이 있기 때문에 각각의 상황을 잘 파악해서 사용하는게 중요합니다.

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함