[php] 정규식 (Regular Expression)

[php] 정규식 (Regular Expression) updated_at: 2023-05-17 16:48

Php 정규식 패턴(Regular Expression)

패턴 구분 문자

정규식의 시작과 끝을 알릴때는 통상 /(슬래쉬)을 사용하나 PHP에서는 | (파이프) 혹은 @ 등을 사용합니다.(기타 다양한 특수문자 #, &, !, ~,등 정규식 패턴등에 사용하지 않는 문자는 적용가능.)
동일하게 사용되지만 가장 큰 차이점은 / 가 문장중에 나올때, 가령 url을 정규식으로 처리할때 역슬래쉬()를 넣어주어서 정규식가 다르다는 것을 인식 시켜주어야 하지만
|(파이프 혹은 기타 특수문자)를 사용하면 그러지 않아도 된다는 장접이 있습니다.(다른 언어의 정규식 패턴과의 호환을 생각하면 / 을 사용하는 것을 추천드림)

$pattern = '|<[^>]+>(.*)</[^>]+>|U';
$pattern = '/<[^>]+>(.*)<\/[^>]+>/U'; // '/' 로 정규식을 표현할때는 </[ 을 <\/[ 이렇게 역슬래쉬를 추가하여야 정상적으로 동작합니다.

정규식 패턴(Regular Expression) 기호

위치지정

  • ^ : 문자열의 시작
  • $ : 문자열의 끝

단일문자

  • . : 임의 한 문자 (줄바꿈 문자를 제외한 모든 단일 문자)

반복성

  • ? : 0 또는 1회 만 매치 (아예 없거나 1회 만 나타남, 최대 1회)
  • * : 선행 패턴을 만족하는 문자(열)가 최소 0회 이상 반복 매치
  • + : 선행 패턴을 만족하는 문자(열)가 최소 1회 이상
  • {n} : 정확히 n개 존재
  • {n,m} : n개이상 m개 이하 존재
  • {n, } : 적어도 n개 이상 존재

그룹으로 식 ()

  • (a|b) : a 이거나 b 임
  • (.*) : 어떤 문자든, 몇개든 올수 있슴(0개 혹은 그 이상의 어떤 글자)
  • (.*?) : 어떤 문자든, 몇개든 올수 있슴(0개 혹은 그 이상의 어떤 글자)

문자 클래스 []

  • [\d] 또는 [0-9] : 모든 숫자

  • [\D] 또는 [^0-9] : 숫자가 아닌 모든 문자

  • [\w] : 모든 일반 문자

  • [\W] : 일반 문자가 아닌 모든 문자

  • [a-z] : a부터 z까지 문자중 한 문자와 맞는 패턴

  • [^패터] : 문자 클래스 내의 ^은 부정을 뜻합니다.

  • [^a-z] : 소문자 a 에서 z 사이의 어떤 문자도 없슴

  • [a-zA-Z] : a부터 z까지, A부터 Z까지 중 한 문자와 맞는 패턴

  • [^0-9] : 숫자가 아닌 다른 패턴

  • [_a-zA-Z] : _(언더바) 이거나 혹은 대,소문자의 어떤 문자

  • [_4^a-zA-Z] : 언더바이거나 숫자사이거나 혹은 ^ 이거나 a에서 z의 소문자이거나 A에서 Z의 대문자

  • [abc] : a, b, c 중 어느 한 문자와 맞는 패턴

  • [1-6] : 1에서 6사이의 숫자

  • [\w]{3} : 일반 문자가 세 번 이상 연속으로 있는 패턴

  • [\d]{1,5} : 숫자가 1번 이상 5번 이하 연속으로 있는 패턴

  • [\W]{3,} : 일반 문자가 아닌 문자가 3번 이상 연속으로 있는 패턴

  • [c-h] : 소문자 c 에서 h 사이의 문자

  • [D-M] : 대문자 D에서 M 사이의 문자

패턴 변경자(Pattern Modifiers)

패턴 구분 문자인 슬래시(/ or |) 뒤에서 지정 참조

  • i : 대소문자 구분 안함
  • m : 줄 단위 매칭
  • s : .을 줄바꿈 문자에도 매칭
  • x : 이스케이프 또는 문자 클래스 내부를 제외하고, 패턴의 공백문자를 무시
  • e : preg_replace() 만 사용하는 변경자로 변경할 문자열을 PHP 코드로 처리하고, 그 결과를 검색된 문자열의 이용하여 일반적인 치환을 합니다.
  • A : 이 변경자를 지정하면, 패턴을 강제적으로 "고정"합니다
  • D : 이 변경자가 설정되면, 패턴의 달러($) 메타문자는 주어진 문자열의 마지막에만 대응합니다
  • S : 이 변경자를 지정하면, 추가 분석을 행합니다. 현 시점에서, 패턴의 분석은 하나의 고정된 시작 문자를 가지지 않는 비고정 패턴에만 유용합니다
  • U : 이 변경자는 수량 지시의 "greediness"를 뒤집습니다. 그리하여 기본값으로 not greedy하게 합니다. 하지만 "?"가 붙으면 greedy하게 됩니다.
  • X : 이 변경자는 펄과 호환되지 않는 PCRE의 추가 기능을 사용하게 합니다. 패턴의 문자와 결합된 백슬래시가 특별한 의미를 지니지 않을 경우에 에러를 발생시켜서, 차후에 추가 기능을 위해 예약해둡니다.
  • J : 내부 옵션 (?J) 설정은 영역의 PCRE_DUPNAMES 옵션을 변경합니다. 서브패턴에 동일한 이름을 허용합니다.
  • u : 정규표현식(패턴 문자열)을 UTF-8 인코딩으로 취급
  • /ims 를 사용할 경우 광범위하게 패턴을 적용할 수 있다(대소문자 구분안하고 줄바꿈까지 매칭)

U

$str = '<b>w</b><b>h</b><b>o</b>';

preg_match_all('/<b>(.*)<\/b>/', $str, $matches1);
preg_match_all('/<b>(.*)<\/b>/U', $str, $matches2);
  • 결과
# U를 사용하지 않은 경우
Array
(
  [0] => Array
    (
      [0] => <b>w</b><b>h</b><b>o</b>
    )

  [1] => Array
    (
      [0] => w</b><b>h</b><b>o
    )
)
# U를 사용한 경우
Array
(
  [0] => Array
    (
      [0] => <b>w</b>
      [1] => <b>h</b>
      [2] => <b>o</b>
    )

  [1] => Array
    (
      [0] => w
      [1] => h
      [2] => o
    )
)

특수문자 이스케이프

  • 특수문자 : ., ,, ?, *, +, ^, $, [, ], (, )
  • 이스케이프 처리 : 특수문자 앞에 역슬래시 를 붙임
  • 만일, 단일 역슬래시와의 매치하려면, 역슬래시를 4번 씀(\\\\)
  • 특수 문자 이스케이프 전용 함수 : preg_quote()

Word Boundaries

word boundaries는 /b 로 표시되며 워드로만 표시되는 것에 한정된다. 즉 특수문자등을 제외하고 하나의 워드와 매치되는 것이다.

$str = 'PHP is awesome. How is CakePHP?';

$pattern = '/PHP/';
preg_match_all($pattern, $str, $matches);

$pattern = '/\bPHP\b/';
preg_match_all($pattern, $str, $matches);

$str = 'PHP is awesome. How is Cake PHP?';
$pattern = '/\bPHP\b/';
preg_match_all($pattern, $str, $matches);
  • 결과
// 모든 PHP를 가져온다.
Array
(
  [0] => Array
    (
      [0] => PHP
      [1] => PHP
    )
)
// 워드 중에 PHP만 있는 것을 가져온다.
Array
(
  [0] => Array
    (
      [0] => PHP
    )
)
// ? 와 같은 것은 제외하고 결과값을 가져온다.
Array
(
  [0] => Array
    (
      [0] => PHP
      [1] => PHP
    )
)

예제

^

$string = 'https://www.onstory.fun/index.php';
$pattern1 = '|^(https?://)?([^/]+)|'; // ^ 가 있을 경우
$pattern2 = '|(https?://)?([^/]+)|'; // ^ 가 없을 경우
  • 결과
# ^ 가 있을 경우
[[0] => https://www.onstory.fun] 

# ^ 가 없을 경우
[
  [0] => https://www.onstory.fun 
  [1] => index.php
]
            


^.{2}[a-z]{1,2}_?[0-9]*([1-6]|[a-f])[^1-9]{2}a+$

^.{2}: 어떤 두자의 문자로 시작하고
[a-z]{1,2} : 소문자a에서 z중 어떤 문자가 1자 내지 2자이고
_?: 언더바가 있거나 없을 수 있고
[0-9]* : 0에서 9까지의 숫자가 0개 혹은 그 이상개 존재하며
([1-6]|[a-f]): 1에서 6사이의 어떤 숫자 혹은 a에서 f 사이의 어떤 소문자가 오며
[^1-9]{2}: 1에서 9사이의 어떤 숫자가 아닌 2자의 글자가오며
a+: a가 한개 혹은 그 이상개 존재한다.

^[1-9]{1}[0-9]*[.]{1}[0-9]{2}$

^[1-9]{1}: 1에서 9사이의 어떤 1개의 숫자로 시작하고
[0-9]*: 0에서 9까지의 숫자가 0개 혹은 그 이상개 존재하며
[.]{1}: 어떤 특정문자가 1개 오고
[0-9]{2}: 0에서 9까지의 숫자가 2개존재

정규표현식 관련 PHP 함수

ereg(), eregi()

PHP 5.3.0 이후로 ereg(), eregi() 는 사라졌다(Deprecated).
대신 preg_match 를 이용하여 값의 존재 여부를 확인할 수 있다.

$pattern = 'abcd';
$str = 'abcdtest';

ereg($pattern, $str); // Bad
preg_match('/'.$pattern.'/', $str) // Good

eregi($pattern, $str); // Bad
preg_match('/'.$pattern.'/i', $str) // Good

ereg_replace()와 eregi_replace()

PHP 5.3.0 이후로 ereg_replace(), eregi_replace() 는 사라졌다(Deprecated).
대신 preg_replace 를 이용하여 처리할 수 있다

$subject = "abcdef";
$pattern = '/^ABC/i'; 
$subject = preg_replace($pattern, '', $subject); 

preg_match vs preg_match_all

두 함수의 사용법은 동일하다. 단 결과에서 preg_match_all 이 모든 결과를 출력하는 반면 preg_match 는 최조 매치된 결과만을 출력한다.

preg_match

'/'문장의 시작과 끝에'/' 를 사용한다.
/i 는 대소문자 구분을 하지 않는 경우 사용한다.
\b 는 단어의 바운드리를 지정한다.

if (preg_match("/\bweb\b/i", "PHP is the web scripting language of choice.")) {
    echo "A match was found."; // result
} else {
    echo "A match was not found.";
}

if (preg_match("/\bweb\b/i", "PHP is the website scripting language of choice.")) {
    echo "A match was found.";
} else {
    echo "A match was not found."; // result -> website는 존재하지만 web 이라는 특정단어가 존재 하지 않는다.
}

url 분리하기

url을 분리하여 프로토콜 및 호스트를 찾는다.

// 시작인 http:// 이고 /이 아닌것이 계속되고 대소문자를 구별하지 않는다.
preg_match("/^(http:\/\/)?([^\/]+)/i", "http://www.onstory.fun/index.html", $matches);
print_r($matches);

Array ( 
   [0] => http://www.onstory.fun 
   [1] => http:// <-- 프로토콜
   [2] => www.onstory.fun <!-- host
)

$host = $matches[2]; // www.onstory.fun
preg_match("/[^\.\/]+\.[^\.\/]+$/", $host, $matches);
echo "도메인은 {$matches[0]}\n"; // 도메인은 onstory.fun
검색엔진의 특정 검색어를 찾아내기
preg_match("/^.*?query=(.*)&/i",
   "http://search.naver.com/search.naver?where=nexearch&query=홈페이지제작&hw=1...", $matches);
print_r($matches);

Array
(
    [0] => http://search.naver.com/search.naver?where=nexearch&query=홈페이지제작&
    [1] => 홈페이지제작
)
// 모든 문자로 시작하고 "query=" 이 있으며 &로 끝나는 값, 여기서는 "()"안의 값이 검색어가 됩니다. 

태그안의 text 가져오기

$pattern = '/<a(.*?)>(.*?)<\/a>/i';
preg_match($pattern, $value, $tmpTitle);

preg_match_all

preg_match_all(pattern, subject, matches)
정규식에의해 검색된 모든 내용을 반환합니다.
결과의 첫번째 배열은 전체 패턴에 매치되는 내용을 나열하고 두번째 부터는 () 로 묶인 첫번째 하위 패턴과 일치하는 문자열 배열입니다.

예제 1

matches[0] 에서는 pattern 에 일치하는 모든 것이 나열되고 matches[1]에서는 () 안의 내용(서브패턴)이 출력됩니다.
만약 위의 pattern 에 () 이 여러개가 있으면 matches[2].... 로 나오겠죠.
subpattern 에 관한 상세설명

$string = '<b>example: </b><div align=left>this is a test</div>';
$pattern = '|<[^>]+>(.*)</[^>]+>|U';

preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
    (
      [0] => <b>example: </b>
      [1] => <div align=left>this is a test</div>
    )
  [1] => Array
    (
      [0] => example: 
      [1] => this is a test
    )
)

subpattern이 출력되면 보기가 좀 번거럽습니다.
이때는 (?:패턴) 처럼 사용하여 subpattern의 출력을 막습니다.

$pattern = '|<[^>]+>(?:.*)</[^>]+>|U';
  • 결과

Array
(
  [0] => Array
    (
      [0] => <b>example: </b>
      [1] => <div align=left>this is a test</div>
    )
)

숫자만 찾기

문자에 포함된 숫자만을 찾는 간단한 예제입니다.

$str = '나는 3학년 5반이고 나이는 17세입니다.';
$pattern = '/\d+/'; //숫자로 시작하고 취소 1회이상 매치
$counts = preg_match_all($pattern, $str, $matches);
  • 결과
$counts: 3
$matches
Array
(
  [0] => Array
   (
    [0] => 3
    [1] => 5
    [2] => 17
   )
)

이메일 찾기

$str = '문의는 [email protected] 또는 [email protected]으로 연락 주시기 바랍니다.';
$pattern = '/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}/';
preg_match_all($pattern, $str, $matches);
  • 결과
Array
(
  [0] => Array
   (
    [0] => [email protected]
    [1] => [email protected]
   )
)

핸드폰번호 검사

$string = '문의는 010-1111-2222 또는 02-333-4444 연락 주세요.';
$pattern = '/\d{2,3}-\d{3,4}-\d{4}/';
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
  (
    [0] => 010-1111-2222
    [1] => 02-333-4444
  )
)

URL 검사

$string = '더 많은 정보는 https://www.onstory.fun에서 확인하세요.';
$pattern = '|(https?://)?([-\w]+\\.[-\w.]+)+\w(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)*|'; // 아래 3가지 패턴은 모두 유사한 패턴을 가진다.
$pattern = '|(https?://)?(www\.)?[a-zA-Z0-9-]+(\.[a-z]{2,})+(/[^\s]*)?|';
$pattern = '/(?:https?:\/\/)?(?:www\.)?[a-zA-Z0-9-]+(?:\.[a-z]{2,})+(?:\/[^\s]*)?/'; // 서브패튼을 출력안함
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
  (
    [0] => https://www.onstory.fun
  )
)

a 태그에서 href 추출

$string = '더 많은 정보는 <a href="https://www.onstory.fun">온스토리</a>에서 확인하세요.';
$pattern ='/href=(\'|\")?([^<>\s\'\"]*)(\'|\"|\s|)/i';
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
    (
      [0] => href="https://www.onstory.fun"
    )

  [1] => Array
    (
      [0] => "
    )

  [2] => Array
    (
      [0] => https://www.onstory.fun
    )

  [3] => Array
    (
      [0] => "
    )
)

img 태그 추출 src 추출

$string = '아름다운 이미지 <img src="https://www.onstory.fun/image.png">를 보셔요';
$pattern ='/<img[^>]*src=["\']?([^>"\']+)["\']?[^>]*>/i';
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
    (
      [0] => <img src="https://www.onstory.fun/image.png">
    )

  [1] => Array
    (
      [0] => https://www.onstory.fun/image.png
    )
)

태그 및 태그내용 추출

$string = '<b>bold text</b><a href=onstory.html>click me</a>';
$pattern = '/(<([\w]+)[^>]*>)(.*)(<\/\\2>)/';
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
    (
      [0] => <b>bold text</b>
      [1] => <a href=onstory.html>click me</a>
    )
  [1] => Array
    (
      [0] => <b>
      [1] => <a href=onstory.html>
    )
  [2] => Array
    (
      [0] => b
      [1] => a
    )
  [3] => Array
    (
      [0] => bold text
      [1] => click me
    )
  [4] => Array
    (
      [0] => </b>
      [1] => </a>
    )
)

태그내용 추출

$string = "<b>bold text</b><a href=onstory.html>click me</a>";
$pattern = '|<[^>]+>(.*)<[^>]+>|U';
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
    (
      [0] => <b>bold text</b>
      [1] => <a href=onstory.html>click me</a>
    )
  [1] => Array
    (
      [0] => bold text
      [1] => click me
    )
)

날짜 추출

$string = "오늘은 20230517일 입니다.";
$pattern = '/([0-9]{4})([0-9]{2})([0-9]{2})/';
preg_match_all($pattern, $string, $matches);
  • 결과
Array
(
  [0] => Array
    (
      [0] => 20230517
    )

  [1] => Array
    (
      [0] => 2023
    )

  [2] => Array
    (
      [0] => 05
    )

  [3] => Array
    (
      [0] => 17
    )
)

preg_replace

preg_replace 는 패턴으로 찾은 문자를 치환할때 자주 사용한다.

ereg_replace()

php5.3부터 ereg_replace라는 함수가 deprecated 되었다.

Replacement 표기법

replacement를 표기할때는 아래처럼 3가지 모두 가능하다.
${num} $num \num

$string = 'April 15, 2023';
$pattern = '/(\w+) (\d+), (\d+)/i';
$replacement = '${1}, \\2, $3'; 
echo preg_replace($pattern, $replacement, $string);// April, 15, 2023

영문자, 숫자, _, . 이외 모든 문자 제거

preg_replace("/[^a-zA-Z0-9_\.]/", "", string);

연속된 날짜를 '-' 로 구분하기

$yyyymmdd = '20230517';
$date = preg_replace("/([0-9]{4})([0-9]{2})([0-9]{2})$/", "\\1-\\2-\\3", $yyyymmdd);
echo $date; // 2023-05-17

특정 태그 제거 정규식

tag 만제거
$string = preg_replace("/<tag[^>]*>/ims", '', $string);
$string = preg_replace("/<\/tag>/ims", '', $string);
$string = '나는 <a href="https://onstory.fun">프로그래머</a> 입니다.';
$string = preg_replace("/<a[^>]*>/i", '', $string);
$string = preg_replace("/<\/a>/i", '', $string); // 나는 프로그래머 입니다.
tag 및 tag 안의 내용 제거
$string = preg_replace("/<tag(.*?)<\/tag>/ims", '', $string);
$string = '나는 <a href="https://onstory.fun">프로그래머</a> 입니다.';
$string = preg_replace("/<a(.*?)<\/a>/is", '', $string); // 나는  입니다.
tag 중 닫힘없는 테그 제거
$string = preg_replace("|<tag[^>]*>(.*?)|ims", "", $string);
$string = '이름 : <input type="text" name="adf">';
$string = preg_replace("|<input[^>]*>(.*?)|ims", "", $string); // 이름 :
attribute 제거
$string = preg_replace("|attribute=([^\s^>]+)|ims", '', $string);
$string = '나는 <div onclick="javascript:abc()" class="none"> 프로그래머</div> 입니다.';
$string = preg_replace("|onclick=([^\s^>]+)|ims", '', $string); // 나는 <div  class="none"> 프로그래머</div> 입니다.
특정 attribute 를 가진 태그 및 내용제거

$str = preg_replace('/(<div class="my-class"\>.*?<\/div>)/ims', '', $str); // 특정클래스(my-class) 가 있으면 안의 내용 까지 제거
$str = preg_replace('~<a([^>]*)(class\\s*=\\s*["\']my-class["\'])([^>]*)>(.*?)</a>~i', '', $str); // 특정클래스(my-class) 가 있으면 안의 내용 까지 제거


$str = preg_replace('!<center>-(.*?)\/center>!is', "", $str); // 특정 태그 제거 

$str = preg_replace('/\[[^]]*\]/','',$str); //[] 삭제및 안의 내용 삭제
모든 개행문자 제거
$str = preg_replace('/\v+/', ' ', $str); // 모든 개행문자를 ' ' 로 변경
연속된 공백 1개로
$str=preg_replace("/\s{2,}/"," ",$str);
태그안에 style= 속성 제거
$str=preg_replace("/ zzstyle=([^\"\']+) /"," ", $str); // style=border:0... 따옴표가 없을때
$str=preg_replace("/ style=(\"|\')?([^\"\']+)(\"|\')?/","", $str); // style="border:0..." 따옴표 있을때
태그안의 width=, height= 속성 제거
$str=preg_replace("/ width=(\"|\')?\d+(\"|\')?/", "", $str);
$str=preg_replace("/ height=(\"|\')?\d+(\"|\')?/", "", $str);
a 태그에서 title 추출
$pattern ="/<a[^>]*title=[\"']?([^>\"']+)[\"']?[^>]*>/i";
html tag 제거 정규식
$content = preg_replace("(\<(/?[^\>]+)\>)", "", $content);

특정 엘리멘트 밑에 추출 정규식

preg_match('/<div id="my2">(.*?)<\/div>/is', $contents, $html);
preg_match("/\<span id=\"moonseller\"\>[^>]\<\/span\>/", $contents, $html);
평점을 남겨주세요
평점 : 5.0
총 투표수 : 1

질문 및 답글