[정규표현식] 정규표현식 정리
정규표현식(正規表現式, 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, 찾는 패턴을 하나만 찾지 말고 계속 해서 찾음. 탐욕적 수량자처럼 일치하는 구간을 늘리는 것이 아니라 일치하는 패턴의 개수가 늘어남 |
m |
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); |
제거 |
$STRING=str_replace(" "," ",$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를 이용하여 공부를 진행해보자.
참고문헌
- Google Analytics Regular Expressions Cheat Sheet
- Regex quick reference
- [개발자 강좌] 정규표현식 (정규식; Regex)
- Microsoft Developer Network 정규식 구문