오늘은 정규표현식에 대해 정리해보려고 합니다.
매주 토요일 진행하는 코드스테이츠 오프토이시간에 정규표현식을 활용하시는 분들이 점차 많아지면서, 빨리 배워놓지 않으면 놓치는 부분이 많아질 것 같더라구요!
일단은 쉽게 이해하기 위해서 생활코딩의 강의(정규표현식 패턴들)와 강의에서 참조한 ZVON.org 사이트를 통해서 먼저 공부했어요. 블로깅도 이 자료들 위주로 진행하려고 합니다!
정규표현식 연습 regexr이 잘되어있어서 여기서 주로 연습했어요.
(regexr에서는 \A, \Z 적용이 안되서 이 부분은 regex101에서 확인했어요!)
(regexr 화면)
* 예시에서 정규표현식으로 선택된 부분은 [] 로 표기하였습니다.
정규표현식은 여타 코드와 마찬가지로 대소문자 구분을 명확히 합니다. 하나라도 맞지 않으면 가차없이 필터링 해버리죠.
1) 정규표현식 : Hello
First match : [Hello], world
All matches : [Hello], world
2) 정규표현식 : hello
First match : hello, world (no 선택)
All matches : hello, world (no 선택)
"Hello"와 h만 소문자로 바꾼 "hello"로 정규표현식을 달리해보면, 정확히 대문자까지 매치된 [Hello]만 선택 됩니다. 소문자로 시작한 예시는 선택 받지 못했습니다. 꼭 대소문자를 구분해야 합니다.
1번의 대소문자 구분처럼 빈칸 " "도 완벽하게 구분해줘야 합니다. 이건 쉬우니까 간단히 예시만 보고 바로 3번으로 넘어갈게요!
1) 정규표현식 : Hello, world
First match : [Hello, world]
All matches : [Hello, world]
>2) 정규표현식 : Hello, world
First match : Hello, (빈칸 2개) world
All matches : Hello, (빈칸 2개) world
\* velog에서는 빈칸 여러개가 허용이 안됨..
"^"은 숫자 6 key의 위에 위치한 부호인데, 정규표현식에서는 라인의 앞쪽에 있는 요소를 선택할 때 쓰입니다. 반대로 "$"는 숫자 4 key의 위에 위치해있고, 정규표현식에서는 라인의 뒤쪽에 있는 요소를 선택할 때 쓰입니다.
1) 정규표현식 : ^you
First match : [you] are you
All matches : [you] are you
2) 정규표현식 : you$
First match : you are [you]
All matches : you are [you]
각각의 정의에 맞게, "^you"는 문장의 맨 앞에 있는 "you"를, "you$"는 맨 뒤에 있는 "you"를 가져오고, 다른 위치에 있는 "you"는 건들지 않는 것을 알 수 있어요. 자기 할 일만 하고 땡!
이 친구들은 "\A", "\Z"랑 헷갈릴 수 있는데 뒤에서 비교해서 보여드릴게요!
escape는 "^"과 같은 특수문자는 아니고 용법을 의미하는데요, 특수문자 자체를 선택 요소로 사용하고 싶을 때 활용합니다. 예를 들어 정규표현식에 "^$"가 표현된다면, 둘 다 특수문자라 아무 것도 선택되지 않거든요.
이 때 백슬래시(" \ ")를 활용하면 됩니다. 사용하고 싶은 특수문자 앞에 백슬래시를 사용하면, 해당 특수문자를 의미있는 문법적 요소가 아니라 하나의 문자로 인식하게 해줍니다. 예시로 한 번 살펴볼게요.
1) 정규표현식 : ^$
First match : $100$\-\$200$ => no 선택
All matches : $100$\-\$200$ => no 선택
2) 정규표현식 : \$
First match : [$]100$\-\$200$
All matches : [$]100[$]\-\[$]200[$]
3) 정규표현식 : ^\$
First match : [$]100$\-\$200$
All matches : [$]100$\-\$200$
4) 정규표현식 : \$$
First match : $100$\-\$200[$]
All matches : $100$\-\$200[$]
5) 정규표현식 : \\
First match : $100$[\]-\$200$
All matches : $100$[\]-[\]$200$
보시는 바와 같이 "^"과 "$" 모두 백슬래시가 앞에 놓이면 일반 문자로 인식되어 다른 문자들처럼 정규표현식이 잘 적용되는 것을 알 수 있습니다.
"."는 모든 글자와 매칭됩니다. 정말 모든 글자! 예시로 볼게요.
1) 정규표현식 : .
First match : [t]his is point
All matches : [this is point]
2) 정규표현식 : .....
First match : [this ]is point
All matchies : [this ][is po]int
1번에서는 모든 글자가 선택의 대상이 되었습니다. 다만 한 가지 유의할 점은 2번처럼 "."의 갯수가 지정될 때 인데요, 갯수가 지정되면 해당 갯수의 글자마다 블록이 생성되고, 그 나머지는 선택이 되지 않습니다.총 13글자로 10글자까지만 선택을 받게된거죠!
그리고 "."도 다른 특수문자들처럼 문자 그대로 사용하기 위해서는 escape 처리를 해줘야 합니다. 이것도 아래 예시로 확인해 볼게요.
1) 정규표현식 : .
First match : [O].K.
All matches : [O.K.]
2) 정규표현식 : \.
First match : O[.]K.
All matches : O[.]K[.]
3) 정규표현식 : \..\.
First match : O[.K.]
All matches : O[.K.]
1번의 "."는 모든 글자를 의미하므로 제시된 모든 글자를 선택했습니다. 반면 2번에선 백슬래시로 escaping 해주니 "."를 글자 그대로 보고 선택해줬네요! 3번의 경우도 마찬가지구요. 조금씩 재밌어지네요...!
"[]"용법은 이 중에 있는건 다 골라잡아! 용법입니다. 대괄호 안에 있는 글자 중 제시된 문장에 있으면 모조리 선택해버리는데요, 한 가지 주의할 점은 있습니다. 대괄호 안에 몇개의 문자가 들어가든, 대괄호 한 개는 하나의 글자를 의미한다는 점! 예시로 확인해 보시죠.
1) 정규표현식 : [aft]
First match : I [a]m your father
All matches : I [a]m your [f][a][t]her
2) 정규표현식 : [aft][hir]
First match : I am your fa[th]er
All matches : I am your fa[th]er
1번에서는 하나의 대괄호만 사용하였고, 이에 맞게 조건을 만족하는 각 알파벳들이 선택되었습니다. 그리고 2번에서는 2개의 대괄호가 표현되었고, 순서와 알파벳 조건을 만족하는 "th" 만이 선택 되었죠. 대괄호는 하나의 문자라는 점, 그리고 여러 대괄호일 경우 순서가 중요하다는 점, 기억해주세요!
범위로 선택하기.. 이거 참 좋은 기능이에요. 이전까지 A~Z 중 하나를 선택하려면 [ABCD...XYZ]를 다 써야했다면, [A-Z]로 쓰면 끝나는 miracle이...!
벌써 눈치채셨을지 모르지만, 범위를 선택하려면 대괄호 안에 해당 범위의 첫번째 문자와 "-",그리고 마지막 문자를 표기하면 됩니다. 일단 알파벳, 숫자 되고 한글도 됩니다! (한글은 [ㄱ-힇]으로 전체 범위 표기 가능) 예시를 볼까요?
1) 정규표현식 : [E-H]
First match : ABCD[E]FGHI abcdefghi
All matches : ABCD[E][F][G][H]I abcdefghi
2) 정규표현식 : [EFGH]
First match : ABCD[E]FGHI abcdefghi
All matches : ABCD[E][F][G][H]I abcdefghi
3) 정규표현식 : [a-d]
First match : ABCDEFGHI [a]bcdefghi
All matches : ABCDEFGHI [a][b][c][d]efghi
4) 정규표현식 : [3-5]
First match : ABCDEFGHI abcdefghi 12[3]456
All matches : ABCDEFGHI abcdefghi 12[3][4][5]6
5) 정규표현식 : [E-Ha-d3-5]
First match : ABCD[E]FGHI abcdefghi 123456
All matches : ABCD[E][F][G][H]I [a][b][c][d]efghi 12[3][4][5]6
예시가 많았네요. 각각 범위에 속하는 한글자씩을 선택해서 보여준 것을 알 수 있습니다. "[E-Ha-d3-5]" 이 정규표현식처럼 여러 범위를 한 번에 표기해도 여전히 먹힌다는 걸 알 수 있죠? 처음 봤을 땐 이게 뭔 소린가 했는데 이제 보니까 다중 범위더라구요!
앞에서 살펴본 "^"은 해당 글자로 시작하는 부분을 선택하기 위한 용도로 쓰였는데요, 대괄호 안에서는 다른 역할을 합니다. 해당 문자들을 제외하고 선택하라는 의미를 가지는 "^"을 예시를 통해 확인해 보겠습니다.
정규표현식 : [^CDghi3-5]
First match : [A]BCDEFGHI abcdefghi 123456
All matches : [A][B]CD[E][F][G][H][I][ ][a][b][c][d][e][f]ghi[ ][1][2]345[6]
괄호 칠게 많네요.. 예시를 잘못 든듯... 예시를 보시면 아시겠지만, 정규표현식으로 표현한 범위 앞에 "^"을 추가했더니 해당 범위의 글자들이 모두 선택에서 제외된 것을 볼 수 있습니다. 똑같은 특수문자면 다 똑같은 기능을 하면 좋겠지만, 효율성을 위해 다른 기능을 넣었겠죠...? 이렇게 기억할게 하나 더 늘어났습니다.
"[]"를 사용한 용법에서는 한 글자만 표현할 수 있어서 불편했다면, "()"를 통해서 다중글자를 표현할 수 있습니다. 또, "|"를 통해 조건을 적용할 수도 있죠. 예시를 보겠습니다.
1) 정규표현식 : (on|ues|rida)
First match : M[on]day Tuesday Friday
All matches : M[on]day T[ues]day F[rid]ay
2) 정규표현식 : (Mon|Tues|Fri)day
First match : [Monday] Tuesday Friday
All matches : [Monday] [Tuesday] [Friday]
3) 정규표현식 : ..(nd|esd|id)day
First match : [Monday] Tuesday Friday
All matches : [Monday] [Tuesday] [Friday]
일단 "|"는 javascript에서와 마찬가지로 or의 의미를 가집니다. 1번을 보면, "on" or "ues" or "rida"를 주어진 문장에서 찾아서 표시했습니다. 2,3번과 같이 소괄호 앞뒤로 연결되는 글자를 추가하면 앞에서 공부했던 기능들이 똑같이 구현되는 것을 알 수 있죠! 어때요, 유용할 것 같죠?
수량자는 한 글자가 몇 번이나 나타날지 구체화 시켜주는 역할을 하는데요, 이번에 알아볼 수량자는 "", "+", "?" 이 3개 입니다. 먼저 ""는 앞에 있는 글자가 0개 이상 있음을 의미합니다. "+"는 1개 이상일 때 사용하고, 마지막으로 "?"는 0개나 1개일 때 사용합니다. 예시로 한 번 볼까요?
1) 정규표현식 : a*b
First match : [aab]c abc bc
All matches : [aab]c [ab]c [b]c
2) 정규표현식 : a+b
First match : [aab]c abc bc
All matches : [aab]c [ab]c bc
3) 정규표현식 : a?b
First match : a[ab]c abc bc
All matches : a[ab]c [ab]c [b]c
각 수량자에 따라서 "b"와 그 앞에 있는 "a"가 선택되는 방식이 달라지는 모습을 볼 수 있습니다.
각 수량자의 예시를 몇 개 더 살펴볼게요.
1) 정규표현식 : .*
First match : [-@- *** -- "*" -- *** -@-]
All matches : [-@- *** -- "*" -- *** -@-]
-> 모든 글자를 한 번에 선택
2) 정규표현식 : -A*-
First match : -@- *** [--] "*" -- *** -@-
All matches : -@- *** [--] "*" [--] *** -@-
-> "A"가 없어도 되므로 "--"가 선택 됨
3) 정규표현식 : [-@]*
First match : [-@-] *** -- "*" -- *** -@-
All matches : [-@-] *** [--] "*" [--] *** [-@-]
-> 얘가 좀 헷갈렸는데, 괄호 안에 있는 글자가 여러번 반복된다는 관점에서 "-@-"도 선택 됨
1) 정규표현식 : \*+
First match : -@@@- [*] ** - - "*" -- * ** -@@@-
All matches : -@@@- [*] [**] - - "[*]" -- [*] [**] -@@@-
-> 1개 이상의 "*"에 선택 됨
2) 정규표현식 : -@+-
First match : [-@@@-] * ** - - "*" -- * ** -@@@-
All matches : [-@@@-] * ** - - "*" -- * ** [-@@@-]
-> -- 사이에 "@"가 하나 이상 있어야 함
3) 정규표현식 : [^ ]+
First match : [-@@@-] * ** - - "*" -- * ** -@@@-
All matches : [-@@@-] [*] [**] [-] [-] ["*"] [--] [*] [**] [-@@@-]
-> 공백을 제외한 1개 이상의 모든 글자가 선택 대상이 됨
1) 정규표현식 : -X?XX?X
First match : -[-XX]-@-XX-@@-XX-@@@-XX-@@@@-XX-@@-@@-
All matches : -[-XX]-@[-XX]-@@[-XX]-@@@[-XX]-@@@@-XX-@@-@@-
-> -XX -XXX -XXXX 세가지를 표현한 식
2) 정규표현식 : -@?@?@?-
First match : [-]-XX-@-XX-@@-XX-@@@-XX-@@@@-XX-@@-@@-
All matches : [--]XX[-@-]XX[-@@-]XX[-@@@-]XX-@@@@-XX[-@@-]@@-
-> -- -@- -@@- -@@@- 네가지를 표현한 식
3) 정규표현식 : [^@]@?@
First match : --XX[-@]-XX-@@-XX-@@@-XX-@@@@-XX-@@-@@-
All matches : --XX[-@]-XX[-@@]-XX[-@@]@-XX[-@@]@@-XX[-@@][-@@]-
-> @@ @ 앞에 "@"를 제외한 다른 글자가 위치하면 선택
선택하고자 하는 글자의 수량을 범위로 특정하려면 "{}"을 활용하면 됩니다. "x{m}" 이렇게 쓰면 m개의 x를 선택한다는 뜻이고, "x{m,n}" 이렇게 쓰면 m개 이상 n개 이하의 x를 선택한다는 뜻 입니다. 직관적이죠? 참고로 "x{m,}" 이렇게 쓰면 m 개 이상을 선택 합니다. 반대인 "x{,n}"은 작동 안하더라구요.
1) 정규표현식 : .{5}
First match : [Are y]ou drunken? please go to bed.
All matches : [Are y][ou dr][unken][? ple][ase g][o to ]bed.
-> 매 5개 글자마자 선택 됨
2) 정규표현식 : [ork]{1,3}
First match : Wh[ooo]! Are you drunken? please go to bed.
All matches : Wh[ooo]! A[r]e y[o]u d[r]un[k]en? please g[o] to bed.
-> ork가 순서 상관 없이 1개 이상 3개 이하로 나열됐을 때 선택 됨
3) 정규표현식 : [a-z]{3,}
First match : W[hooo]! Are you drunken? please go to bed.
All matches : W[hooo]! Are [you] [drunken]? [please] go to [bed].
-> 3개 이상 알파벳 소문자 나열 시 선택 됨
위 내용을 보면서 느끼셨는지 모르겠지만, 전에 언급한 수량자들을 "{}"을 활용해서도 표현할 수 있습니다.
"*"는 {0,}로 표현할 수 있고, "+"는 {1,}로, 그리고 "?"는 {0,1}으로 표현할 수 있죠! 이렇게 말이죠.
1) 정규표현식 : AB*A
First match : [AA] ABA ABBA ABBBA
All matches : [AA] [ABA] [ABBA] [ABBBA]
2) 정규표현식 : AB{0,}A
First match : [AA] ABA ABBA ABBBA
All matches : [AA] [ABA] [ABBA] [ABBBA]
3) 정규표현식 : AB+A
First match : AA [ABA] ABBA ABBBA
All matches : AA [ABA] [ABBA] [ABBBA]
4) 정규표현식 : AB{1,}A
First match : AA [ABA] ABBA ABBBA
All matches : AA [ABA] [ABBA] [ABBBA]
5) 정규표현식 : AB?A
First match : [AA] ABA ABBA ABBBA
All matches : [AA] [ABA] ABBA ABBBA
6) 정규표현식 : AB{0,1}A
First match : [AA] ABA ABBA ABBBA
All matches : [AA] [ABA] ABBA ABBBA
수량자는 기본적으로 탐욕적(greedy)으로 설계되었다고 합니다. 여기서 탐욕적이라는게 무슨 뜻이냐면, 조건을 만족하는 가장 큰 덩어리를 찾는다는 뜻인데요, 예를 들면, 아래와 같은 상황을 들 수 있습니다.
정규표현식 : <div>.*</div>
match : [<div>what is</div><div>your name</div>]
위와 같은 경우, 두 개의 div 블록으로 나눠져야할 것 같지만, greedy한 수량자의 특성 때문에 가장 앞의
lazy한 수량자의 기본 개념은 조건을 만족하는 최소한의 범위를 선택하게 한다는 건데요, 이 역할은 수량자 뒤에 "?"가 붙음으로써 수행됩니다. 예시로 한 번 볼까요?
1) 정규표현식 : r.*
First match : One [ring to bring them all and in the darkness bind them]
All matches : One [ring to bring them all and in the darkness bind them]
-> 처음 r이 나온 후 그 뒤 모든 글자가 선택되었습니다. 매우 탐욕적이죠?
2) 정규표현식 : r.*?
First match : One [r]ing to bring them all and in the darkness bind them
All matches : One [r]ing to b[r]ing them all and in the da[r]kness bind them
-> 1)과 달리 수량자 뒤에 "?"가 붙었습니다. "*"는 아시다시피 0개 이상의 글자를 선택하는 수량자인데요, 여기에 "?"가 붙으면 조건을 만족하는 최소한의 조건, 즉 0개를 만족하는 범위가 선택됩니다. 이 정규표현식에서는 r 뒤에 아무것도 선택되지 않은(0개) "r"이 되겠죠?
3) 정규표현식 : r.+
First match : One [ring to bring them all and in the darkness bind them]
All matches : One [ring to bring them all and in the darkness bind them]
-> 1)과 동일한 결과
4) 정규표현식 : r.+?
First match : One [ri]ng to bring them all and in the darkness bind them
All matches : One [ri]ng to b[ri]ng them all and in the da[rk]ness bind them
-> 3)에 "?"가 추가로 붙었고, 기존 "+" 수량자의 최소 조건인 1개를 만족하는 범위가 선택됩니다. 여기서는 "r"과 추가 1글자가 됩니다.
5) 정규표현식 : r.?
First match : One [ri]ng to bring them all and in the darkness bind them
All matches : One [ri]ng to b[ri]ng them all and in the da[rk]ness bind them
-> "r" + 0 또는 1글자가 선택 됨
6) 정규표현식 : r.??
First match : One [r]ing to bring them all and in the darkness bind them
All matches : One [r]ing to b[r]ing them all and in the da[r]kness bind them
-> 5)에 "?"가 추가로 붙었고, 최소한의 조건을 만족하는 "r" + 글자가 선택 됨
예시와 함께 "?"의 역할을 살펴봤습니다. 하나 주의할 점은 생활코딩을 비롯한 다른 블로그들에서는 "*?"는 0개, "+?"는 1개, "??"는 0개를 선택하는 걸로 설명하시던데, 이렇게만 이해하고 있으면 이후에 실제 적용 사례를 공부할 때 이해가 잘 안가더라구요. 위에서 잠시 봤던
사례를 가져와서 설명을 더 해볼게요.//"?" 적용 전
정규표현식 : <div>.*</div>
match : [<div>what is</div><div>your name</div>]
//"?" 적용 후
정규표현식 : <div>.*?</div>
match : [<div>what is</div>][<div>your name</div>]
이 사례를 공부할 때, "*?"는 0개라고 접근을 하니 아무리 생각해도 말이 안되더라구요. 그래서 0개가 아니라, "조건을 만족하는 최소한의 범위"라는 개념으로 접근하니 고민이 풀렸습니다.
안에 0개 이상의 글자를 포함하는 조건을 만족할 때 블록이 선택되므로, what is 블록에서 먼저 조건이 만족되어 "?" 적용 전 처럼 전체가 선택되지 않습니다.요 개념은 잘 살펴보지 않으면 나중에 고생할거 같으니... 여러번 봐두려구요.
\w
"\w" 를 사용하면 알파벳+숫자+"_"를 한 번에 표현할 수 있어요. 뭔가 다 되는 만능키 같은데요, 예시로 한 번 보겠습니다.
1) 정규표현식 : \w
First match : [A]1 B2 c3 d_4 e:5 ffGG77--__--
All matches : [A][1] [B][2] [c][3] [d][_][4] [e]:[5] [f][f][G][G][7][7]--[_][_]--
-> 모든 알파벳, 숫자, "_"가 한 글자씩 선택 됨
2) 정규표현식 : \w*
First match : [A1] B2 c3 d_4 e:5 ffGG77--__--
All matches : [A1] [B2] [c3] [d_4] [e]:[5] [ffGG77]--[__]--
3) 정규표현식 : [a-z]\w*
First match : A1 B2 [c3] d_4 e:5 ffGG77--__--
All matches : A1 B2 [c3] [d_4] [e]:5 [ffGG77]--__--
4) 정규표현식 : \w{5}
First match : A1 B2 c3 d_4 e:5 [ffGG7]7--__--
All matches : A1 B2 c3 d_4 e:5 [ffGG7]7--__--
5) 정규표현식 : [A-z0-9_]
First match : [A]1 B2 c3 d_4 e:5 ffGG77--__--
All matches : [A][1] [B][2] [c][3] [d][_][4] [e]:[5] [f][f][G][G][7][7]--[_][_]--
-> \w 과 결과적으로 동일함. \w의 경우 먹히지 않는 프로그램 언어가 있다고 해요!
이와는 반대로 "\W"(대문자)를 하면 그 외 글자들을 가져올 수 있습니다. 알파벳, 숫자, "_"를 제외한 모든 글자요!
1) 정규표현식 : \W
First match : AS[ ]_34:AS11.23 @#$ %12^*
All matches : AS[ ]_34[:]AS11[.]23[ ][@][#][$][ ][%]12[^][*]
->
2) 정규표현식 : \w
First match : [A]S _34:AS11.23 @#$ %12^*
All matches : [A][S] [_][3][4]:[A][S][1][1].[2][3] @#$ %[1][2]^*
->
3) 정규표현식 : [^A-z0-9_]
First match : AS[ ]_34:AS11.23 @#$ %12^*
All matches : AS[ ]_34[:]AS11[.]23[ ][@][#][$][ ][%]12[^][*]
-> 1)과 동일함
\s
"\s"를 사용하면 " " 이런 공백을 표현할 수 있고, 반대로 "\S"(대문자)를 사용하면, 공백 외 모두를 표현할 수 있어요. 간단하게 예시만 보고 넘어갈게요.
1) 정규표현식 : \s
First match : This[ ]is mine, don't touch.
All matches : This[ ]is[ ]mine,[ ]don't[ ]touch.
2) 정규표현식 : \S
First match : [T]his is mine, don't touch.
All matches : [T][h][i][s] [i][s] [m][i][n][e][,] [d][o][n]['][t] [t][o][u][c][h][.]
\d
숫자를 표현하는 방법에는 [0-9]도 있지만, "\d"로도 표현할 수 있어요. 반대로 "\D"로는 숫자 외 다른 글자들을 선택할 수 있답니다. 다만, 특정 언어에서는 사용 불가능한 경우도 있다고 하니 참고하시면 좋겠네요 !
1) 정규표현식 : \d
First match : Page [1]23; published: 1234 id=12#24@112
All matches : Page [1][2][3]; published: [1][2][3][4] id=[1][2]#[2][4]@[1][1][2]
2) 정규표현식 : \D
First match : [P]age 123; published: 1234 id=12#24@112
All matches : [P][a][g][e][ ]123[;][ ][p][u][b][l][i][s][h][e][d][:][ ]1234[ ][i][d][=]12[#]24[@]112
3) 정규표현식 : [0-9]
First match : Page [1]23; published: 1234 id=12#24@112
All matches : Page [1][2][3]; published: [1][2][3][4] id=[1][2]#[2][4]@[1][1][2]
-> 1)과 동일
\b
아직 어떤 때 쓰는지는 모르겠으나, "\b"로 단어의 경계를 표현할 수 있습니다. 여기서 "단어"라는건 13번에서 살펴본 "백슬래시 + w"로 표현되는 [A-z0-9_]를 의미합니다. 그래서 단어의 경계는 무슨 뜻이냐... 생활코딩 강의를 봐서는 이해가 안되서 여러 블로그를 좀 뒤져봤는데요, 아래에서 도식화를 한 번 해보겠습니다.
I have a dream. Are you? ; No, I don't have.
이 문장의 단어 경계를 "|"로 표시해보면, 아래처럼 됩니다.
|I| |have| |a| |dream|. |Are| |you|? ; |No|, |I| |don|'|t| |have|.
위에 표시된 "|"를 기준으로 정규표현식이 표현되는데요, 예를 들어 "\b." 라고 하면, "|" 오른쪽에 한 개의 글자가 있는 부분들이 선택됩니다.
"\B"는 이와는 반대로 단어의 경계가 아닌 모든 곳을 표현합니다. 이것도 도식화를 해보면, 아래처럼 됩니다.
I h|a|v|e a d|r|e|a|m.| A|r|e y|o|u?| |;| N|o,| I d|o|n't h|a|v|e.|
이것들을 가지고 예시를 한 번 만들어 볼게요!
1) 정규표현식 : ./b.
First match : [I ]have a dream. Are you? ; No, I don't have.
All matches : [I ]hav[e ][a ]drea[m.] Ar[e ]yo[u?] ;[ N][o,][ I][ d]o[n'][t ]hav[e.]
2) 정규표현식 : ./B.
First match : I [ha]ve a dream. Are you? ; No, I don't have.
All matches : I [ha][ve] a [dr][ea]m[. ][Ar]e [yo]u[? ][; ][No][, ]I [do]n't [ha][ve].
혹시 이해가 안가신다면, 위에서 경계를 도식화 해놓은 부분을 보고 다시 한 번 확인해 보시면 이해가 가실거에요!
\A : 문장 맨 앞을 기준으로 표현
\Z : 문장 맨 뒤를 기준으로 표현
문장 맨 앞, 뒤를 얘기하니, 3번에서 살펴봤던 "^", "$"가 기억 나시는 분이 있으실지도 모르겠는데요, 얘네들과 지금 설명하는 친구들과는 약간 차이가 있습니다.
"^"과 "$"는 멀티라인, 즉 여러 줄로 이루어진 문장의 경우, 각 줄마다의 맨 앞, 맨 뒤에서 표현식에 맞는 글자들을 찾아냅니다. 반면, "백슬래시 + A or Z"는 전체 문장에서의 맨 앞, 맨 뒤에서만 표현식에 맞는 글자들을 찾아냅니다. 예시를 한 번 볼까요?
1) 정규표현식 : ^..
First match :
[aa]aa
aaaa
aaaa
All matches :
[aa]aa
[aa]aa
[aa]aa
2) 정규표현식 : \A..
First match :
[aa]aa
aaaa
aaaa
All matches :
[aa]aa
aaaa
aaaa
3) 정규표현식 : ..$
First match :
aa[aa]
aaaa
aaaa
All matches :
aa[aa]
aa[aa]
aa[aa]
4) 정규표현식 : ..\Z
First match :
aaaa
aaaa
aa[aa]
All matches :
aaaa
aaaa
aa[aa]
이해가 되셨나요~? 되셨으면 좋겠네요!
"?="은 해당 패턴을 포함한 글자를 찾긴 찾는데, 찾은 값을 표기할 때는 해당 패턴을 표기하지 않는 방식입니다. 말로 하면 좀 어려운 것 같은데요, 예시를 들어서 설명해 볼게요.
1) 정규표현식 : \w+(?=X)
First match : [AAA]X---aaax---111
All matches : [AAA]X---aaax---111
-> 정규표현식에서 문자 여러개와 마지막 자리에는 "X"가 포함된 글자를 찾고 있는데요, 이 조건을 만족하는 값은 "AAAX" 입니다. 하지만 매치 값을 표기할 때는 "X"가 빠진 "AAA"만 표기됩니다. 검색할 때만 "X"를 사용하고 표기할 때는 뺀거죠!
2) 정규표현식 : \w+
First match : [AAAX]---aaax---111
All matches : [AAAX]---[aaax]---[111]
-> 패턴 조건이 빠진 결과 입니다.
3) 정규표현식 : \w+(?=\w)
First match : [AAA]X---aaax---111
All matches : [AAA]X---[aaa]x---[11]1
-> 마지막 단어는 빼고 표기되었습니다.
재밌는 표현 방식이네요 ㅎㅎ
이거랑은 조금 다른 패턴으로 "?!"이 있는데요, 이건 특이하게 찾아진 결과값 전체를 선택하지 않습니다.
예시를 들어볼게요.
1) 정규표현식 : AAA(?!X)
First match : AAAX---[AAA]
All matches : AAAX---[AAA]
-> 결과 값을 보면, 패턴 밖에 있는 "AAA"표현식만 반영해서 단어들을 선택하고, 패턴과 정확히 일치하는 "AAAX"는 선택하지 않습니다.
2) 정규표현식 : AAA
First match : [AAA]X---AAA
All matches : [AAA]X---[AAA]
-> 패턴을 빼면, "AAA"가 전부 선택되는걸 볼 수 있죠!
드디어 정리가 끝났네요! 생활코딩 강의 기반으로 이해안가는 부분은 추가로 확인해 가면서 글을 작성 해봤습니다. 글이 길어진 감이 있는데, 나중에 필요한 걸 찾을 때 여러 페이지로 나눠져 있으면 불편할 것 같아서, 좀 길더라도 글 하나 안에 다 담았습니다.
정리랍시고 하긴 했지만, 실 사용 예시들을 보면 여전히 이해 안가는 것들이 많아서... 사용해보다가 새로운 걸 알게 되면 다른 글로 또 정리해야겠습니다.
정규표현식이 어려우셨던 분들에게 조금이라도 도움이 됐길 바라면서... 그럼 이만...!