고급 패턴과 특수 문자
1. 게으른 수량자
물음표는 없을 수도 있다는 의미입니다. 기본 수량자는 "탐욕적"(greedy)으로 동작하여 가능한 많은 문자를 매칭하려 합니다. 수량자 뒤에 ?
를 붙이면 없을 수도 있기 때문에 "게으른"(lazy)으로 표현합니다. Lazy Quantifier라고 불립니다.
010[- .]?\d{4}[- .]?\d{4} // 전화번호 패턴
010[- .]?\d{4}[- .]?\d{4} // 전화번호 패턴
이렇게 실습을 먼저 진행해보겠습니다. 물음표는 없을 수도 있기 때문에 아래와 같은 패턴도 매칭됩니다.
01012345678
01012345678
1.1 탐욕적 vs 게으른 매칭
// 탐욕적 매칭
/h.*o/gm // "hello world hello" -> "hello world hello"
// 게으른 매칭
/h.*?o/gm // "hello world hello" -> "hello", "hello"
// 탐욕적 매칭
/h.*o/gm // "hello world hello" -> "hello world hello"
// 게으른 매칭
/h.*?o/gm // "hello world hello" -> "hello", "hello"
1.2 모든 게으른 수량자
*? // 0회 이상 (최소한으로 매칭)
+? // 1회 이상 (최소한으로 매칭)
?? // 0회 또는 1회 (최소한으로 매칭)
{n}? // 정확히 n회 (의미 없음)
{n,}? // 최소 n회 (최소한으로 매칭)
{n,m}?// n회 이상 m회 이하 (최소한으로 매칭)
*? // 0회 이상 (최소한으로 매칭)
+? // 1회 이상 (최소한으로 매칭)
?? // 0회 또는 1회 (최소한으로 매칭)
{n}? // 정확히 n회 (의미 없음)
{n,}? // 최소 n회 (최소한으로 매칭)
{n,m}?// n회 이상 m회 이하 (최소한으로 매칭)
2. 전방탐색과 후방탐색
2.1 전방탐색
전방탐색(Lookahead)은 특정 패턴 뒤에 다른 패턴이 오는지 확인하되, 해당 패턴은 매칭에 포함하지 않습니다.
// 긍정 전방탐색 (?=) - 뒤에 특정 패턴이 있는 경우
/\w+(?=@)/gm // 이메일에서 @ 앞부분만 매칭
// 부정 전방탐색 (?!) - 뒤에 특정 패턴이 없는 경우
/\d+(?!\.)/gm // 뒤에 점이 오지 않는 숫자들
// 긍정 전방탐색 (?=) - 뒤에 특정 패턴이 있는 경우
/\w+(?=@)/gm // 이메일에서 @ 앞부분만 매칭
// 부정 전방탐색 (?!) - 뒤에 특정 패턴이 없는 경우
/\d+(?!\.)/gm // 뒤에 점이 오지 않는 숫자들
아래와 같이 활용할 수 있습니다.
// 링크 텍스트만 추출
/\[.*?(?=\])/gm // [링크](http://...) -> "링크" 부분만 추출
// 파일명만 추출 (확장자 제외)
/\w+(?=\.txt)/gm // file.txt -> "file"
// 링크 텍스트만 추출
/\[.*?(?=\])/gm // [링크](http://...) -> "링크" 부분만 추출
// 파일명만 추출 (확장자 제외)
/\w+(?=\.txt)/gm // file.txt -> "file"
2.2 후방탐색
후방탐색(Lookbehind)은 특정 패턴 앞에 다른 패턴이 있는지 확인하되, 해당 패턴은 매칭에 포함하지 않습니다.
// 긍정 후방탐색 (?<=) - 앞에 특정 패턴이 있는 경우
/(?<=\$)\d+/gm // 달러 기호 뒤의 숫자만 매칭
// 부정 후방탐색 (?<!) - 앞에 특정 패턴이 없는 경우
/(?<!-)\d+/gm // 하이픈 앞에 없는 숫자들
// 긍정 후방탐색 (?<=) - 앞에 특정 패턴이 있는 경우
/(?<=\$)\d+/gm // 달러 기호 뒤의 숫자만 매칭
// 부정 후방탐색 (?<!) - 앞에 특정 패턴이 없는 경우
/(?<!-)\d+/gm // 하이픈 앞에 없는 숫자들
아래와 같이 활용할 수 있습니다.
// 통화 금액에서 숫자만 추출
/(?<=\$)\d+(\.\d{2})?/gm // $12.34 -> "12.34"
// 도메인에서 최상위 도메인 추출
/(?<=\.)[a-z]+$/gm // example.com -> "com"
// 특정 접두사가 없는 단어 찾기
/(?<!re)\w+/gm // reload -> "load"
// 통화 금액에서 숫자만 추출
/(?<=\$)\d+(\.\d{2})?/gm // $12.34 -> "12.34"
// 도메인에서 최상위 도메인 추출
/(?<=\.)[a-z]+$/gm // example.com -> "com"
// 특정 접두사가 없는 단어 찾기
/(?<!re)\w+/gm // reload -> "load"
3. 특수 문자열 패턴
3.1 단어 경계
단어의 경계를 나타내는 특수 문자입니다.
/\bword\b/gm // "word"는 매칭, "words"는 매칭 안됨
/\bhello\b/gm // 독립된 "hello" 단어만 매칭
/\bword\b/gm // "word"는 매칭, "words"는 매칭 안됨
/\bhello\b/gm // 독립된 "hello" 단어만 매칭
아래와 같이 활용할 수 있습니다.
// 완전한 단어 찾기
/\bcat\b/gm // "cat" 매칭, "category" 매칭 안됨
// 특정 접두사로 시작하는 단어
/\bre\w+/gm // "reload", "rewrite" 등
// 특정 접미사로 끝나는 단어
/\w+ing\b/gm // "loading", "writing" 등
// 완전한 단어 찾기
/\bcat\b/gm // "cat" 매칭, "category" 매칭 안됨
// 특정 접두사로 시작하는 단어
/\bre\w+/gm // "reload", "rewrite" 등
// 특정 접미사로 끝나는 단어
/\w+ing\b/gm // "loading", "writing" 등
3.2 줄의 시작과 끝
이미 배웠지만, 멀티라인 모드에서의 동작을 자세히 살펴봅니다.
// 줄의 시작 ^
/^hello/gm // 각 줄의 시작에 있는 "hello"
// 줄의 끝 $
/world$/gm // 각 줄의 끝에 있는 "world"
// 전체 문자열의 시작과 끝
/^hello.*world$/gm // "hello"로 시작하고 "world"로 끝나는 한 줄
// 줄의 시작 ^
/^hello/gm // 각 줄의 시작에 있는 "hello"
// 줄의 끝 $
/world$/gm // 각 줄의 끝에 있는 "world"
// 전체 문자열의 시작과 끝
/^hello.*world$/gm // "hello"로 시작하고 "world"로 끝나는 한 줄
4. 연습 예제
4.1 문제
- HTML 태그 내의 내용만 추출하는 패턴을 만드세요.
- 이메일 주소에서 도메인 부분만 추출하는 패턴을 만드세요.
- 금액 표시에서 통화 기호를 제외한 숫자만 추출하는 패턴을 만드세요.
- 독립된 숫자 단어만 찾는 패턴을 만드세요.
- URL에서 프로토콜을 제외한 도메인만 추출하는 패턴을 만드세요.
4.2 해답
/<.*?>(.*?)<\/.*?>/gm
/(?<=@)[\w.-]+/gm
/(?<=\$)\d+(\.\d{2})?/gm
/\b\d+\b/gm
/(?<=:\/\/)([^\/]+)/gm
⚠️ 실습 시 주의사항
- 전방/후방 탐색은 매칭 결과에 포함되지 않습니다.
- 게으른 수량자는 필요한 최소한의 문자만 매칭합니다.
- 단어 경계는 공백, 문장 부호 등과 구분됩니다.
- 전방/후방 탐색의 패턴이 복잡해질수록 성능에 영향을 줄 수 있습니다.