[정규표현식] 정규표현식 정리

류명운

·

2016. 9. 5. 04:36

반응형

[정규표현식] 정규표현식 정리

 

정규표현식(正規表現式, Regular Expression)은 문자열을 처리하는 방법 중의 하나로 특정한 조건의 문자를 '검색'하거나 '추출', '치환'하는 과정을 매우 간편하게 처리 할 수 있도록 하는 수단이다.

 

대표적인 정규표현식의 여러 패턴들과 예제를 살펴보자.

기본 메타 문자

기호  설명
.  모든 문자 일치
|  OR 왼쪽 문자(혹은 패턴) 혹은 오른쪽 문자(혹은 패턴)과 일치
[]  문자 집합 구성원 중 하나와 일치
 ex) [abc] : a 혹은 b 혹은 c
[^]   문자 집합 구성원을 제외하고 일치, [^abc] : a, b, c 제외한 모든 문자
-  범위 정의 ([A-Z]와 같은 형태로 대문자 A에서 Z사이의 문자를 의미)
\  다음에 오는 문자를 이스케이프

 

기본 메타 문자는 글자 하나의 의미를 말한다. 이 자체만으로는 큰 활용도가 없고 다른 기능들과 같이 쓰일 때 의미가 있다.

수량자

기호  설명
*  앞의 문자나 부분식이 0개 이상 탐욕적으로 찾기
*?  탐욕적 수량자 *를 게으른(lazy) 수량자로 바꿔 찾기
+  앞의 문자나 부분식을 하나 이상 탐욕적으로 찾기
+?  탐욕적 수량자 +를 제으른(lazy) 수량자로 바꿔 찾기
?  앞의 문자나 부분식을 0개나 1개 찾기
{n}  앞의 문자나 부분식이 정확히 n번 일치하는 경우 찾기
{m,n}  앞의 문자나 부분식이 m번에서 n번 일치하는 경우 찾기
{n,}  앞의 문자나 부분식이 n번 이상인 경우를 탐욕적으로 찾기
{n,}?   탐욕적 수량자 {n,}를 게으른(lazy) 수량자로 바꿔 찾기

 

문자 하나가 아니라 조건에 해당하는 붙어있는 여러개의 문자를 찾는다는 것이 핵심이다. *, + 의 차이를 이해해야 한다.

  • *는 없는 경우가 포함되고
  • +는 무조건 하나는 있어야 한다는 의미다.

다음으로 탐욕적 수량자게으른 수량자를 이해해야 한다. 이 부분은 매우 중요하면서도 처음엔 어려울 수 있는 부분이다.

  • 탐욕적 수량자의 핵심은 조건에 맞지 않을 때까지 하나의 패턴으로 인식하는 것이고
  • 게으른 수량자의 핵심은 조건에 맞으면 욕심부리지 말고 거기서 끝내는 것이다.

위치 지정

기호  설명
^  입력 문자열의 시작에서 그 다음 나오는 문자나 부분식과 일치하는지 검사
\A  어떤 정규식에서는 ^의 역할을 함
$  문자열의 끝과 일치
\Z  어떤 정규식에서는 $의 역할을 함
\b  단어 경계(단어와 공백 사이의 위치)와 일치
 예) 'st\b'는 "test"의 st는 찾지만, "tester"의 st는 찾지 않음
 * test test에서 test 사이의 빈공간인 space와는 다른 의미
\B  \b와 반대(비단어 경계)로 일치. 즉 "\b"의 예제의 반대 결과가 나옴

 

글자 자체를 찾는 물리적 의미가 아니라 내부적인 의미를 찾는 논리적 수행만 한다. 위치 지정 또한 이 자체만으로는 의미가 없으며 ^, \b, \B 다음이나 \b, \B, $ 전에 나오는 문자나 부분식과 함께 동작한다.

특수한 문자

기호  설명
[\b]

 역스페이스
\cx

 x로 표시된 제어문자 찾기. x는 [A-Za-z] 이어야 함. 아니면 c는 리터럴 'c'로 간주
 예) \cM = Contorl-M 이나 캐리지 리턴 문자
\d

 모든 숫자와 일치. [0-9]와 동일
\D

 \d와 반대. [^0-9]와 동일
\f

 용지 공급 문자 찾기. 페이지 넘기기(formfeed) = \x0a, \cL
\n

 줄 바꿈 문자 찾기. \x0a, \cJ
\r

 캐리지 리턴. \x0d, \cM
\s

 공백, 택, 용지 공급 등과 같은 문자 찾기. [\f\n\r\t\v]
\S

 \s와 반대로 일치. [^\f\n\r\t\v]
\t

 탭 문자 찾기. \x09, \cI
\v

 세로 탭 문자 찾기. \x0b, \cK
\w

 영숫자 문자나 밑줄과 일치. [a-zA-Z0-9_]
\W

 \w와 반대로 일치. [^a-zA-Z0-9_]
\xn

 n으로 표시된 16진수 이스케이프 값과 일치. 16진수는 정확히 2자리여야 함
\0n

 n으로 표시된 8진수 숫자와 일치. 가급적 사용하지 않는 것이 좋음

 

하나하나 [] 안에 넣어주어야 하는 문자들을 미리 정의해둔 것으로 이해하면 된다. []를 사용하는 것과 동작의 차이는 없지만 패턴 글자의 수를 줄일 수 잇기 때문에 알아두면 보기 편하다.

역참조와 전후방탐색

패턴  설명
(pattern)  하위 표현식 정의. 패턴을 찾아 일치하는 항목을 캡처하는 부분식. 가로 자체 '('나')'를 찾고 싶으면 '\'를 붙여 이스케이프하면 된다. '\(', '\)' 
\1  첫 번째 일치한 하위 표현식, 두 번째 일치한 하위 표현식은 \2로 표기
(?=pattern)  전방탐색
(?!pattern)  부정형 전방탐색
?(BR)true  조건 지정
?(BR)true|false  else 표현식 조건 지정

 

()와 []는 전혀 다른 의미라는 사실을 머리에 각인해야 한다. 이 파트가 정규표현식에서 가장 어려운 부분이며 프로그래밍 할 때 가장 많이 사용되는 부분이기도 하다. 

 

가로로 묶은 하위 표현식은 역참조 시 사용하기도 하지만, 프로그래밍에서는 하위표현식에 해당하는 문자열을 배열이나 그룹으로 따로 제공해 주기 때문에 유용하게 사용할 수 있다.

대소문자 변환

 설명
\E  \L 혹은 \U 변환을 끝냄
\I  다음에 오는 글자를 소문자로 변환
\L  \E를 만날 때까지 모든 문자를 소문자로 변환
\u  다음에 오는 글자를 대문자로 변환
\U  \E를 만날 때까지 모든 문자를 대문자로 변환

 

문자열을 찾은 후 이를 바꾸는 용도로 사용한다. 

옵션

기호  설명
i  ignore case, 대소문자를 무시하여 찾음. 굳이 [a-zA-Z]라고 할 필요가 없음
g  global, 찾는 패턴을 하나만 찾지 말고 계속 해서 찾음. 탐욕적 수량자처럼 일치하는 구간을 늘리는 것이 아니라 일치하는 패턴의 개수가 늘어남
 multiline, 다중행 모드라고도 하며 라인 별로 처리하는 것이 아니라 입력 문자열에 줄바꿈이 있어도 이를 특수문자로 변환하여 하나로 봄. 가장 큰 차이를 경험할 수 있는 기능은 ^와 $임.

 

여러 조건에 해당하는 단어 찾기, 여러 조건에 해당하는 단어를 다른 단어로 바꾸기, 복잡한 패턴 속에서 필요한 내용만 추출할 때 사용한다.

우선 순위

 연산자  설명
 \  이스케이프
 (), (?:), (?=), []  괄호와 대괄호
 *, +, ?, {n}, {n,}, {n,m}  수량자
 ^, $, \anymetacharacter  앵커와 시퀀스
 |  교체

 

정규식은 수식과 유사하게 왼쪽에서 오른쪽으로 계산되고 우선 순위를 따른다. 위 표에서는 정규식 연산자를 우선 순위가 높은 것부터 순서대로 설명하였다.

 

* 문자는 교체 연산자보다 더 높은 우선순위를 가지므로 예를 들어 "m|food"로 "m"이나 "food"를 찾을 수 있다.

정규표현식 적용 예제

예 1) 기본적인 정규식

정규표현식  설명
abc  abc 가 있는 것
^abc  abc 로 시작하는 것
abc$  abc 로 끝나는 것
^abc$  ^abc$ abc 로 시작하고 끝나는 것
[abc]  a,b,c 전부 중 하나를 포함한 경우
[a-z]  a 에서 z 중 하나를 포함한 경우
^[0-9]  숫자 0~9 중 하나로 시작하는 패턴을 찾는 것
[^0-9]  숫자가 들어있지 않는 패턴을 찾는 것
^[^0-9]  숫자가 들어있지 않은 문자로 시작하는 패턴을 찾는 것
a{3}  a 의 3번 반복인 aaa 인 것
a{3,}  a 가 3번이상 반복인 것
[0-9]{2}  두 자리 숫자
abc[7-9]{2}  abc 를 포함하고 7~9까지 숫자 중 2자리가 포함하는 것

예 2) 대표적으로 많이 사용하는 정규식

패턴 설명   정규표현식 패턴
이메일  "/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/"
집전화번호  "/^(070|02|031|032|033|041|042|043|051|052|053|054|055|061|062|
063|064)-\d{3,4}-\d{4}$/u"
휴대폰번호  "/^(010|011|016|017|018|019)-\d{3,4}-\d{4}$/u"
우편번호  "/^\d{3}-?\d{3}$/u"
아이디  "/^[a-zA-Z]\w{2,7}$/u"
주민등록번호  "/^\d{2}[0-1]\d[0-3]\d-?[1-6]\d{6}$/u"

예 3) 태그 제거(HTML)

패턴 설명  정규표현식 패턴 
iframe 제거  $STRING=preg_replace("!<iframe(.*?)<\/iframe>!is","",$STRING);
&nbsp; 제거  $STRING=str_replace("&nbsp;"," ",$STRING);
복수 공백 하나로  $STRING=preg_replace("/\s{2,}/"," ",$STRING)
style= 제거  $STRING=preg_replace("/ style=([^\"\']+) /"," ",$STRING); // style=border:0...
style="" 제거  $STRING=preg_replace("/ style=(\"|\')?([^\"\']+)(\"|\')?/","",$STRING); // style="border:0..."
width= 제거  $STRING=preg_replace("/ width=(\"|\')?\d+(\"|\')?/","",$STRING);
height= 제거  $STRING=preg_replace("/ height=(\"|\')?\d+(\"|\')?/","",$STRING);

예 4) 자바 기반 자주 쓰이는 정규표현식

import java.util.regex.Pattern;   /** 문자열의 형식을 검사하는 기능을 갖는 클래스 */ public class PatternChecker {           /** 숫자 모양에 대한 형식 검사 */     public static boolean isNum(String str) {         return Pattern.matches("^[0-9]*$", str);     }           /** 영문으로만 구성되었는지에 대한 형식 검사 */     public static boolean isEng(String str) {         return Pattern.matches("^[a-zA-Z]*$", str);     }           /** 한글로만 구성되었는지에 대한 형식 검사 */     public static boolean isKor(String str) {         return Pattern.matches("^[ㄱ-ㅎ가-힣]*$", str);     }           /** 영문과 숫자로만 구성되었는지에 대한 형식 검사 */     public static boolean isEngNum(String str) {         return Pattern.matches("^[a-zA-Z0-9]*$", str);     }           /** 한글과 숫자로만 구성되었는지에 대한 형식 검사 */     public static boolean isKorNum(String str) {         return Pattern.matches("^[ㄱ-ㅎ가-힣0-9]*$", str);     }           /** 이메일 형식인지에 대한 검사 --> "아이디@도메인"의 형식을 충족해야 한다. */     public static boolean isEmail(String str) {         return Pattern.matches("[0-9a-zA-Z]+(.[_a-z0-9-]+)*@(?:\\w+\\.)+\\w+$", str);     }           /** 핸드폰번호인지에 대한 형식검사.         반드시 앞자리가 010,010,016~9사이를 충족해야 하며,         각 부분에 대한 자리수도 충족시켜야 한다.         "-"는 허용하지 않는다. */     public static boolean isCellPhone(String str) {         return Pattern.matches("^01(?:0|1|[6-9])(?:\\d{3}|\\d{4})\\d{4}$", str);     }           /** 전화번호인지에 대한 형식검사. 각 연결부는 "-"로 구분되어야 한다.         각 부분에 대한 자리수도 충족시켜야 한다.         "-"는 허용하지 않는다. */     public static boolean isTel(String str) {         return Pattern.matches("^\\d{2,3}-\\d{3,4}-\\d{4}$", str);     }           /** 주민번호에 대한 글자수 및 뒷자리 첫글자가 1~4의 범위에 있는지에 대한 검사.         "-"는 허용하지 않는다. */     public static boolean isJumin(String str) {         return Pattern.matches("^\\d{6}[1-4]\\d{6}", str);     }           /** 아이피주소 형식에 대한 검사 */     public static boolean isIP(String str) {         return Pattern.matches("([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})", str);     } }

 

이 밖에 보다 자세한 정규표현식의 사용법은 아래 기재된 zvon의 정규표현식 tutorials를 이용하여 공부를 진행해보자.

 

참고문헌

반응형