들어가며: (조금 다른 이야기입니다만, 건너뛰려면 클릭 >>)
요약에서 언급했듯이, 정규 표현식은 방대한 지식 체계입니다. 단순히 메타 문자 표 하나로 정의되거나 몇 마디 말로 설명할 수 있는 것이 아닙니다.
어떤 이는 이렇게 평했습니다. "...컴퓨터 발전 역사상 위대한 것들이 몇 가지 등장했다면, 정규 표현식(Regular Expression)이 그중 하나일 것입니다. Web, Lisp, 해시 알고리즘, UNIX, 관계형 모델, 객체 지향 등도 이 목록에 포함되겠지만, 이런 것들은 절대 20개 항목을 넘지 않을 것입니다..."
이런 말도 여러분의 관심을 끌기에는 부족할 수 있습니다. 정규 표현식에 대해 들어본 적이 있고, 메타 문자 표를 보며 기존의 표현식을 이해할 수도 있지만, 실제 개발에서는 거의 사용하지 않을 수도 있기 때문입니다...
실제로 그렇습니다. 그렇다면 정규 표현식은 아직 살아있나요? 어디에 있나요?
정답은 정규 표현식이 이미 프로그래밍 언어, 운영 체제 및 관련 애플리케이션에 스며들었다는 것입니다. 예를 들어, 많은 고급 언어에서 제공하는 String.find()와 같은 메서드나, 많은 운영 체제에서 제공하는 파일 내용 검색 명령(Linux의 grep 명령 등)은 모두 정규 표현식과 관련이 있습니다.
그렇다면 정규 표현식이 이미 "사라져(스며들어)" 버렸는데, 우리가 이를 배울 필요가 있을까요? 당연히 있습니다. 정규 표현식은 하나의 기술이며, 기술을 이해하는 것의 의미는 도구를 익히는 것보다 훨씬 큽니다.
6.어떻게 하면 효율적인 정규 표현식을 작성할 수 있을까?
8.요약
9.부록【메타 문자 표】【모드 제어 문자 표】【특수 메타 문자 표】
1. 정규 표현식 작동 원리
정규 표현식이 대상 문자열에 적용되는 구체적인 과정은 다음과 같습니다.
-
정규 표현식 컴파일
정규 표현식의 구문 정확성을 검사하고, 올바르면 내부 형식으로 컴파일합니다.
-
전동(Transmission) 시작
전동 장치가 정규 표현식 엔진을 대상 문자열의 시작 위치에 "배치"합니다.
P.S. "전동"을 간단히 설명하자면, 엔진 내부의 메커니즘입니다. 예를 들어 [abc]를 family라는 문자열에 적용할 때, 먼저 첫 번째 위치인 f를 시도하고 실패하면 두 번째 위치인 a로 이동하여 성공하고 매칭을 종료합니다. 이때 누가 이러한 "위치별" 처리(첫 번째 시도 후 실패 시 두 번째 시도...)를 제어할까요? 바로 전동 장치입니다.
-
요소 검사
엔진이 정규 표현식과 텍스트의 매칭을 시도합니다. 단순히 앞으로 나아가는 것뿐만 아니라 백트래킹 과정도 포함됩니다(백트래킹은 매우 중요한 개념으로 뒤에서 자세히 설명합니다).
매칭 결과 도출
매칭 결과(성공 또는 실패)를 확정합니다. 이 과정은 엔진의 유형에 따라 달라집니다. 예를 들어 첫 번째 완전 매칭을 찾으면 바로 성공을 반환하거나, 첫 번째 적합한 문자열을 찾은 후에도 계속 찾아 가장 긴 문자열을 반환하기도 합니다.
-
구동 과정
현재 위치에서 적절한 매칭을 찾지 못하면, 전동 장치가 엔진을 구동하여 현재 위치의 다음 문자부터 새로운 시도를 시작합니다.
매칭 완전 실패
전동 장치가 엔진을 문자열 끝까지 구동했음에도 적절한 매칭을 찾지 못하면 매칭 실패를 선언합니다(간단히 말해 처음부터 끝까지 맞는 게 없으면 실패입니다. 여기서는 내부 원리에 가깝게 설명하기 위해 다소 딱딱하게 묘사했습니다).
2. 정규 표현식 엔진
소위 정규 표현식 엔진 유형은 사실 하나의 분류입니다. 앞에서 언급했듯이 정규 표현식은 기술이며, 누구나 이를 사용해 문제를 해결할 수 있지만 해결 방식은 저마다 다릅니다. 즉, 정규 표현식의 구체적인 구현과 규칙이 모두 다르다는 뜻입니다. 오랜 발전 과정을 거쳐 몇 가지 유파가 형성되었으며, 각 유파가 내세우는 규칙도 다릅니다.
일반적인 유파(엔진 유형)는 다음과 같습니다.
- NFA (비결정적 유한 자동계)
- DFA
- POSIX NFA
- DFA, NFA 혼합형
각 엔진의 분류 기준을 상세히 알 필요는 없습니다. 서로 간의 차이점과 우리가 자주 사용하는 도구가 어디에 속하는지만 이해하면 됩니다. 매우 간단합니다.
NFA
해당 도구: Java, GUN Emacs, grep, dotNet, PHP, Python, Ruby 등
차이점: NFA는 '정규 표현식 주도형' 엔진이라고 할 수 있습니다. 매칭 효율이 정규 표현식과 밀접하게 연관되어 있기 때문입니다(예: 표현식 내 다중 선택 분기의 순서).
DFA
해당 도구: awk, egrep, flex, lex, MySQL, Procmail 등
차이점: DFA는 '텍스트 주도형' 엔진이라고 부릅니다. 매칭 효율이 오직 텍스트(대상 문자열)와만 연관됩니다(동등하지만 형식이 다른 표현식의 효율이 같습니다. 예: [a-d]와 [abcd]. NFA에서는 이 둘의 효율이 다르며 일반적으로 전자가 더 좋습니��).
POSIX NFA
해당 도구: mawk, Mortice Kern System's utilities 등
차이점: 매칭 성공 여부와 상관없이 모든 가능성을 시도하여 가장 긴 매칭 문자열을 찾으려고 합니다.
DFA, NFA 혼합형
해당 도구: GUN awk, GUN grep/egrep, Tcl
차이점: 가장 훌륭하고 성숙한 엔진이라고 할 수 있습니다. 내부 최적화가 잘 되어 있어 DFA와 NFA의 장점을 모두 갖추고 있지만, 현재 이 엔진을 사용하는 도구는 많지 않습니다.
지금까지의 내용을 요약하자면 다음과 같습니다.
정규 표현식을 지원하는 도구를 사용하기 전에 해당 엔진의 유형을 아는 것이 매우 중요합니다. 엔진마다 구체적인 작동 방식이 다르기 때문입니다. 예를 들어 PHP의 세 가지 정규 표현식 라이브러리는 모두 NFA형이며 매칭이 표현식과 밀접하게 연관되어 있으므로, 효율성을 높이기 위해 표현식을 적절히 최적화해야 합니다.
3. 전후방 탐색(Lookaround)
[이 내용은 정규 표현식의 아주 작은 부분이므로 굳이 따로 떼어낼 필요는 없지만, '전후방 탐색'을 모르거나 들어는 봤어도 생소하고 어렵게 느끼는 분들이 있어 별도로 논의해 보겠습니다(절대 어렵지 않습니다).]
1. "전후방 탐색"이란 무엇인가?
단어 그대로 주위를 둘러보는 것입니다. 정규 표현식의 전후방 탐색도 마찬가지입니다. 어떤 위치로 이동한 후, 먼저 좌우를 살펴보고 그 위치가 우리가 찾는 위치인지 확인하는 것입니다.
예를 들어, (this|that)으로 "there is a boy lying under that tree."를 매칭한다고 해봅시다. 이 표현식은 NFA 엔진에서 효율이 매우 낮습니다. 다음과 같이 작동하기 때문입니다.
- 먼저 첫 번째 문자 t를 만나면 this를 검사하는데, i와 e가 맞지 않음을 발견하고 다시 that을 검사하지만 a와 e가 맞지 않음을 발견합니다.
- 다음 위치인 h로 이동하여 this 검사... that 검사...
- 다음 위치인 e로 이동하여...
- ...
수많은 헛수고를 하게 됩니다. 어떻게 최적화할까요?
공통된 접두사를 추출하여(자주 쓰이는 최적화 방식 중 하나입니다) th(is|at)으로 바꿀 수 있습니다.
하지만 여기서는 전후방 탐색을 논의 중이니 이를 사용해 (?=th)(this|that)으로 해결해 봅시다. 앞의 (?=)가 생소하다고요?
괜찮습니다. 이것이 바로 '긍정형 전방 탐색(Positive Lookahead)'입니다. 의미는 다음과 같습니다. 시작부터 뒤로 가다가 th를 만나면 멈추고, (?=th) 뒤의 표현식 부분인 (this|that)과 대조합니다. [반대로 말하면 th를 만나지 못하면 멈추지 않고 계속 뒤로 간다는 뜻입니다... 효율이 달라지는 게 느껴지시나요?]
최적화 후 비교 횟수가 눈에 띄게 줄어듭니다. 물론 이 예제에서 전후방 탐색을 쓰는 건 좀 과할 수 있지만, 사용법을 보여주는 간단한 예시일 뿐이니 너무 따지지는 맙시다.
2. 전후방 탐색의 종류와 역할
| 유형 | 정규 표현식 | 매칭 성공 조건 |
| 긍정형 전방 탐색 | (?=...) | 하위 표현식이 오른쪽 텍스트와 매칭됨 |
| 긍정형 후방 탐색 | (?<=...) | ___________________왼쪽______ |
| 부정형 전방 탐색 | (?!...) | 하위 표현식이 오른쪽 텍스트와 매칭되지 않음 |
| 부정형 후방 탐색 | (?<!...) | ___________________왼쪽______ |
P.S. 위의 왼쪽/오른쪽은 매칭이 진행 중인 현재 위치 기준입니다. 이는 일반적인 매칭과 다릅니다. 예를 들어:
긍정형 전방 탐색 (?=a)abc로 family라는 문자열을 매칭할 때, 시작 위치는 f가 아니라 f의 바로 앞입니다. 왜 그럴까요?
【전후방 탐색 구조는 어떤 문자도 매칭하지 않고, 오직 텍스트 내의 특정 위치만을 매칭하기 때문】입니다. 현재 위치가 f와 a 사이라면 긍정형 전방 탐색이 성공하고, abc를 순차적으로 검사하기 시작합니다.
결론적으로 긍정형 전방 탐색은 실제 비교가 시작되는 위치를 제한하여 시도 횟수를 줄여줍니다.
3. 전후방 탐색의 응용
전후방 탐색은 표현식 최적화나 다른 특수한 상황(전후방 탐색 없이는 해결이 불가능한 경우 등. 물론 대개는 좀 더 복잡한 구조로 대체 가능합니다)에 주로 사용됩니다.
예를 들어, "the land belongs to these animals"에서 단어 the만 매칭하고 these 안의 the는 피하려면 어떻게 해야 할까요?
단어 경계 기호(엔진이 지원한다면)인 \bthe\b를 사용해 전체 매칭을 하면 쉽게 해결됩니다.
하지만 이번 예제에서는 the(?!\w)를 사용해서도 목표를 달성할 수 있습니다. 앞의 the가 these 안의 the를 매칭하더라도, 뒤의 부정형 전방 탐색 (?!\w)이 these를 제외해 버립니다. (여기서 부정형 전방 탐색은 e 뒤에 단어 문자가 올 수 없음을 제한합니다. 구체적으로 \w는 [a-zA-Z0-9]와 같으므로 여기서는 완벽하지 않을 수 있지만 개념 설명에는 충분합니다.)
4. 백트래킹 (최적화를 언급하기 전, 백트래킹은 절대적으로 중요한 문제입니다)
간단히 말해, 백트래킹(Backtracking)은 시도하지 않은 분기로 되돌아가는 것(또는 대기 상태로 돌아가는 것)을 의미합니다. 정규 표현식에 익숙하지 않은 분들에게는 전자가, 더 정확하게는 후자의 설명이 적합합니다.
간단한 예로, .*!를 사용해 "An idel youth, a needy age!", an old saying said. 라는 문자열을 매칭해 봅시다.
- 먼저, .을 수식하는 *는 임의의 문자를 개수 상관없이 매칭할 수 있습니다(.은 모든 문자, *는 수량). 그리고 *는 탐욕적(greedy) 매칭, 즉 매칭 우선 방식이어서 가능한 한 길게 매칭하려고 합니다.
- 따라서 .*은 전체 문자열(A부터 마침표까지)을 매칭합니다. 이때 뒤의 !를 검사해 보니 매칭할 수 없습니다. 어떻게 할까요?
- .*이 매칭한 문자열 중 일부를 돌려주어 !가 매칭될 기회를 주어야 합니다. 문장 끝의 마침표를 돌려주었지만, !는 여전히 매칭되지 않습니다.
- 계속 돌려줍니다. 이번에는 d입니다. 매칭되지 않습니다.
- ...
- age 뒤의 !가 돌려받아졌을 때 비로소 매칭에 성공합니다.
.*이 전체 문자열을 점유했다가 !를 위해 억지로 돌려주는 이 일련의 과정이 바로 백트래킹(엔진의 구동이 뒤로 돌아가는 것)입니다.
이러한 백트래킹은 명백히 무의미하며 시간을 낭비합니다. 우리가 수행하는 최적화 작업의 상당 부분은 백트래킹 횟수를 줄이는 데 집중됩니다.
다른 관점에서 보면, 백트래���을 줄이는 것은 매칭 효율을 높이는 것, 즉 엔진이 작동을 시작해서 결과를 내놓기까지의 시간을 단축하는 것입니다. 이것이 바로 최적화가 아닐까요?
5. 정규 표현식 최적화
효율 지표
정규 표현식의 효율을 측정할 때 주로 두 가지 지표를 참고합니다. 시도(비교) 횟수와 백트래킹 횟수입니다.
표현식의 정확성이 보장된다는 전제하에, 시도 횟수와 백트래킹 횟수는 적을수록 좋습니다. 횟수가 적다는 것은 더 빠르게 매칭을 찾거나 실패를 더 빠르게 알 수 있음을 의미합니다.
최적화 작업
최적화 작업은 두 가지 방향이 있습니다.
특정 작업 속도 향상
이는 엔진의 구체적인 내부 구현과 연관됩니다. 예를 들어 일반적인 NFA 엔진에서 [\d]는 [0-9]보다 빠르고, [0-9]는 [0123456789]보다 빠릅니다.
불필요한 작업 방지
즉, 정밀한 제한입니다. 위에서 언급한 전후방 탐색 예시처럼 매칭 시작 위치를 제한하면 효율을 크게 높일 수 있습니다.
물론 이런 최적화를 할 때는 저울질이 필요합니다. 위치를 제한하는 데 너무 많은 시간을 써서 오히려 매칭 효율이 떨어진다면 그런 최적화는 바람직하지 않습니다.
최적화 여부와 그 정도는 구체적인 애플리케이션 시나리오에 맞춰 판단해야 합니다.
자주 쓰이는 최적화 방법
최적화 방법은 매우 많지만, 여기서는 가장 흔히 쓰이는 몇 가지만 나열합니다(관심 있는 분들은 관련 서적을 참고하세요).
불필요한 괄호 제거
많은 경우 괄호()는 텍스트를 포착(capture)하기 위해서가 아니라 단순히 범위를 제한하기 위해 사용됩니다. 이때는 비포착 그룹(?:)을 사용하여 메모리 사용량을 줄이고 효율을 크게 높여야 합니다.
불필요한 문자 클래스 제거
[.]과 같은 문자 클래스로 단일 특수 문자를 표현하는 습관이 있는 분들이 있는데, 이를 \.으로 바꾸는 것이 좋습니다. 마찬가지로 [*] -> \* 등도 해당됩니다.
반복 컴파일 피하기
다른 도구에서 정규 표현식을 사용할 때 주의해야 할 점입니다. 예를 들어 Java에서 정규 표현식을 텍스트에 적용하려면 먼저 컴파일해야 합니다. 동일한 표현식은 한 번만 컴파일하면 되므로, 컴파일 부분을 루프 안에 두어 반복 컴파일로 시간을 낭비하지 않도록 해야 합니다.
시작 앵커 사용
이는 꼭 들여야 할 좋은 습관입니다. 예를 들어, .*으로 시작하는 대부분의 정규 표현식 앞에 ^ 또는 \A를 붙여 행이나 단락의 시작임을 나타낼 수 있습니다. 이렇게 하면 어떤 이점이 있을까요?
일부 구형 엔진에서 이 최적화 효과는 매우 뚜렷합니다. .*이 전체 대상 문자열을 한 번 훑은 뒤 맞는 게 없다고 판단했을 때, 표현식 앞에 ^나 \A가 없다면 엔진은 대상 문자열의 두 번째 문자부터 다시 시도하게 됩니다... 명백히 무의미한 일입니다(첫 번째 시도에서 결과가 나왔는데 굳이 두 번째, n 번째 시도를 할 필요가 없습니다).
성숙한 엔진들은 이런 표현식을 자동으로 최적화합니다. .* 앞에 시작 앵커가 없으면 엔진이 자동으로 시작 위치 플래그를 추가해 무의미한 시도를 방지합니다.
하지만 우리 입장에서는 .* 앞에 시작 플래그를 붙이는 것을 습관화하는 것이 좋습니다.
리터럴 텍스트 독립시키기
예를 들어 [xx*]는 [x+]보다 빠르고, x{3, 5}는 xxxx{0, 2}보다 느리며, th(?:is|at)은 (?:this|that)보다 빠릅니다.
6. 어떻게 하면 효율적인 정규 표현식을 작성할 수 있을까?
정규 표현식 작성 시 다음 단계를 따르는 것이 좋습니다.
- 기대하는 텍스트 매칭
- 기대하지 않는 텍스트 제외
- 제어와 이해가 용이하도록 작성
- 효율성 보장 및 빠른 결과 도출(성공/실패)
앞의 두 단계는 표현식의 정확성을 보장하며, 뒤의 두 단계는 효율성과 사용 편의성 사이의 적절한 절충이 필요합니다. 이것이 정규 표현식 작성의 원칙입니다.
여기에 일반적인 원칙을 잘 설명해 주는 명언이 하나 있습니다. "목욕물 버리려다 아이까지 버리지 마라."
7. 자주 틀리는 몇 가지 포인트
[-./]와 [.-/]와 [./-]의 차이
얼핏 비슷해 보이지만, 첫 번째와 세 번째는 동일하며 현재 위치의 문자가 하이픈, 마침표, 슬래시 중 하나여야 함을 의미합니다.
두 번째 표현식은 잘못되었습니다. 이는 현재 위치의 문자가 마침표(.)에서 슬래시(/) 사이의 모든 문자 중 하나여야 함을 의미합니다(여기서 -는 [a-z]처럼 범위를 나타냅니다). 하지만 마침표와 슬래시 사이에 어떤 문자가 있는지는 문자 집합 환경에 따라 다르며, 유니코드 환경에서는 의도치 않은 이상한 문자들이 포함될 수 있습니다.
따라서 문자 클래스 안에서 -를 쓸 때는 위치를 주의 깊게 살펴 오류를 피해야 합니다.
[] 안팎에서의 ^ 차이
밖에서의 ^는 행의 시작을, $는 행의 끝을 의미합니다. 안에서의 ^는 "부정"([^...] 즉 제외형 문자 클래스) 또는 일반 문자([...^])를 의미합니다.
[ab]*와 (a*|b*)의 차이
둘은 비슷해 보이지만 특수한 상황이 있습니다. 전자는 aba를 매칭할 수 있지만 후자는 그렇지 못합니다. 또한 전자의 효율이 더 높습니다.
수량자(quantifier) 중첩 사용(?+*) 시의 주의점
수량자가 중첩되어 사용될 때는 그 의미를 잘 파악하여 무한 루프(무한 백트래킹)를 피해야 합니다. 예를 들어 "(\\.|[^\\"]+)*"으로 텍스트 내 연속된 쌍따옴표 부분을 매칭하려 할 때, 따옴표 안에 백슬래시로 이스케이프된 쌍따옴표가 포함될 수 있다면 이 표현식은 루프를 유발하여 거의 영원히 결과를 얻지 못할 수 있습니다.
수량자 중첩이 반드시 루프를 일으키는 것은 아니지만, 표현식에 수량자 중첩이 나타날 때는 매우 신중하게 다루어야 합니다.
8. 요약
정규 표현식에 대한 개인적인 견해는 다음과 같습니다.
정규 표현식에 대한 이해가 깊지 않다면, 복잡한 문제를 해결하려 하거나 너무 긴 표현식을 쓰지 않는 것이 좋습니다. 그 안에 숨겨진 함정들은 이해하기 매우 힘들 수 있으며, 완벽한 정규 표현식을 구성하려면 매우 치밀한 사고가 필요하기 때문입니다. 일반적인 상황에서는 프로그래밍 코드를 통해 문자열 매칭을 제어하는 것이 더 쉬울 수 있습니다.
물론 정규 표현식을 아예 쓰지 말라는 뜻은 아닙니다. 특정 상황(예: 텍스트에서 URL 추출 등)에서 정규 표현식은 대체 불가능한 마법 같은 역할을 합니다.
또한 본인이 직접 쓰지 않더라도 다른 사람들이 쓴 표현식을 마주하게 될 것이므로, 정규 표현식을 충분히 이해해 두어야 합니다.
### 참조 매칭흔치 않은 용법으로, 정규 표현식 내에서 이미 매칭된 부분을 참조할 수 있습니다. 예시는 다음과 같습니다.
// js code
var regex = /(\w{2,4}).+\1.+\1/i;
console.log(regex.test('qwer11qwe213234qw')); // true
console.log(regex.test('qwer11qwe213234q')); // false
알 수 없는 반복 시퀀스가 포함된 문자열을 감지할 때 사용합니다(반복 시퀀스가 몇 번 나타나는지는 알지만, 구체적으로 어떤 시퀀스인지는 모를 때). 위 예시에서 반복되는 시퀀스는 qw입니다.
9. 부록【메타 문자 표】【모드 제어 문자 표】【특수 메타 문자 표】
1. 메타 문자 표 (대부분의 도구에서 공통적으로 지원하는 메타 문자)
| 메타 문자 | 이름 | 의미 |
| ^ | 캐럿(Caret) | 행의 시작 위치 |
| $ | 달러 기호 | 행의 끝 위치 |
| . | 마침표(Dot) | 임의의 문자(일반적으로 행 끝의 \n은 제외) |
| [] | 문자 클래스 | 괄호 안의 문자 중 하나(반드시 한 문자와 매칭되어야 함) |
| [^] | 제외형 문자 클래스 | 괄호 안의 문자를 제외한 임의의 한 문자(반드시 한 문자와 매칭되어야 함) |
| \char | 이스케이프 문자 | char의 다른 의미를 나타냄. 예: \^는 시작 위치가 아닌 일반 문자 ^를 의미 |
| () | (포착형) 괄호 | 수량자의 범위 지정 또는 매칭된 텍스트 포착(역참조에서 사용 가능) |
| (?:) | 비포착 그룹 괄호 | 괄호와 기능은 같으나 텍스트를 포착하지 않음 |
| ? | 물음표 | 수량자, 왼쪽 부분이 있어도 되고 없어도 됨 |
| * | 별표 | 수량자, 왼쪽 부분이 0개 이상 있을 수 있음 |
| + | 더하기 기호 | 수량자, 왼쪽 부분이 최소 1번 이상 나타나야 함 |
| {min, max} | 구간 | 수량자, 왼쪽 부분이 최소 min번, 최대 max번 나타남 |
| {num} | 고정 구간 | 수량자, 왼쪽 부분이 정확히 num번 나타나야 함 |
| | | 수직선(Pipe) | '또는'을 의미하며 다중 선택 구조를 구현함 |
| \< | 단어 경계 | 단어의 시작 위치 |
| \> | 단어 경계 | 단어의 끝 위치 |
| \num | 역참조 | n번째 포착 괄호에 의해 매칭된 텍스트(괄호 순서는 여는 괄호 기준임) |
2. 모드 제어 문자 표 (도구마다 다를 수 있는 예시)
| 제어 문자 | 의미 |
| i | 대소문자 무시 매칭 |
| g | 전역 매칭, 첫 번째 매칭 후 멈추지 않고 텍스트 내 모든 매칭을 찾음 |
| x | 자유로운 형식, 정규 표현식을 여러 행으로 나누고 주석을 포함할 수 있음 |
| m | 강화된 앵커 모드, 단락을 논리적 행으로 나누어 ^와 $가 각 행의 시작과 끝에 대응함 |
| s | 단일 행 모드, 마침표(.)가 줄바꿈 문자를 포함한 모든 문자와 매칭됨 |
3. 특수 메타 문자 표 (일부 도구에서 지원하는 특수 메타 문자)
| 메타 문자 | 의미 |
| \d | 숫자, [0-9]와 동일 |
| \D | 숫자가 아닌 문자, [^0-9]와 동일 |
| \w | 숫자 및 영문자, [a-zA-Z0-9]와 동일 |
| \W | 숫자 및 영문자가 아닌 문자, [^a-zA-Z0-9]와 동일 |
| \s | 공백 문자(스페이스, 탭, 캐리지 리턴, 개행 등) |
| \S | 공백 문자가 아닌 문자 |
| \b | 단어 경계, 단어의 시작 또는 끝 위치 |
| (?>...) | 원자적 그룹(Atomic grouping), 매칭된 문자를 절대 돌려주지 않음 |
| ??, +?, *?, {min, max}? | 게으른(lazy) 수량자, 가능한 한 적게 매칭함 |
| ?+, ++, *+, {min, max}+ | 소유적(possessive) 수량자, 원자적 그룹과 의미가 같음 |
본 내용은 필자가 참고 서적을 이해한 내용을 바탕으로 작성되었습니다.
참고 서적: 《정규 표현식 완전 정복 (Mastering Regular Expressions)》(Jeffrey E.F. Friedl 저)
서평: 장 구성과 강조 방식, 레이아웃이 매우 훌륭한 책입니다. 특히 질문을 던지고 페이지를 넘겨야 답이 나오도록 구성된 점이 흥미롭습니다. 정규 표현식을 깊이 있게 이해하는 데 큰 도움이 되니 관심 있는 분들은 읽어보시기 바랍니다.
아직 댓글이 없습니다