[TIL] 정규표현식 알아가기

jomminii_before·2019년 10월 21일
5

오늘은 정규표현식에 대해 정리해보려고 합니다.
매주 토요일 진행하는 코드스테이츠 오프토이시간에 정규표현식을 활용하시는 분들이 점차 많아지면서, 빨리 배워놓지 않으면 놓치는 부분이 많아질 것 같더라구요!

일단은 쉽게 이해하기 위해서 생활코딩의 강의(정규표현식 패턴들)와 강의에서 참조한 ZVON.org 사이트를 통해서 먼저 공부했어요. 블로깅도 이 자료들 위주로 진행하려고 합니다!

정규표현식 연습 regexr이 잘되어있어서 여기서 주로 연습했어요.
(regexr에서는 \A, \Z 적용이 안되서 이 부분은 regex101에서 확인했어요!)

(regexr 화면)

* 예시에서 정규표현식으로 선택된 부분은 [] 로 표기하였습니다.

1.대소문자 구분

정규표현식은 여타 코드와 마찬가지로 대소문자 구분을 명확히 합니다. 하나라도 맞지 않으면 가차없이 필터링 해버리죠.

예시

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]만 선택 됩니다. 소문자로 시작한 예시는 선택 받지 못했습니다. 꼭 대소문자를 구분해야 합니다.

2. 빈칸 구분

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에서는 빈칸 여러개가 허용이 안됨..

3. ^(caret, 캐럿)과 $(dollor, 달러)의 용법

"^"은 숫자 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"랑 헷갈릴 수 있는데 뒤에서 비교해서 보여드릴게요!

4. escape(이스케이프) 활용

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$

보시는 바와 같이 "^"과 "$" 모두 백슬래시가 앞에 놓이면 일반 문자로 인식되어 다른 문자들처럼 정규표현식이 잘 적용되는 것을 알 수 있습니다.

5. .(point, 마침표)의 용법

"."는 모든 글자와 매칭됩니다. 정말 모든 글자! 예시로 볼게요.

예시

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번의 경우도 마찬가지구요. 조금씩 재밌어지네요...!

6."[]"(square bracket, 대괄호)의 용법

"[]"용법은 이 중에 있는건 다 골라잡아! 용법입니다. 대괄호 안에 있는 글자 중 제시된 문장에 있으면 모조리 선택해버리는데요, 한 가지 주의할 점은 있습니다. 대괄호 안에 몇개의 문자가 들어가든, 대괄호 한 개는 하나의 글자를 의미한다는 점! 예시로 확인해 보시죠.

예시

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" 만이 선택 되었죠. 대괄호는 하나의 문자라는 점, 그리고 여러 대괄호일 경우 순서가 중요하다는 점, 기억해주세요!

7. 범위로 선택하기

범위로 선택하기.. 이거 참 좋은 기능이에요. 이전까지 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]" 이 정규표현식처럼 여러 범위를 한 번에 표기해도 여전히 먹힌다는 걸 알 수 있죠? 처음 봤을 땐 이게 뭔 소린가 했는데 이제 보니까 다중 범위더라구요!

8. "[]"(squarebracket, 대괄호) 안에서 ^(caret, 캐럿)이 갖는 의미

앞에서 살펴본 "^"은 해당 글자로 시작하는 부분을 선택하기 위한 용도로 쓰였는데요, 대괄호 안에서는 다른 역할을 합니다. 해당 문자들을 제외하고 선택하라는 의미를 가지는 "^"을 예시를 통해 확인해 보겠습니다.

예시

정규표현식 : [^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]

괄호 칠게 많네요.. 예시를 잘못 든듯... 예시를 보시면 아시겠지만, 정규표현식으로 표현한 범위 앞에 "^"을 추가했더니 해당 범위의 글자들이 모두 선택에서 제외된 것을 볼 수 있습니다. 똑같은 특수문자면 다 똑같은 기능을 하면 좋겠지만, 효율성을 위해 다른 기능을 넣었겠죠...? 이렇게 기억할게 하나 더 늘어났습니다.

9. "()"(parentheses, 소괄호)와 "|"(pipeline, 파이프라인)로 서브패턴 사용하기

"[]"를 사용한 용법에서는 한 글자만 표현할 수 있어서 불편했다면, "()"를 통해서 다중글자를 표현할 수 있습니다. 또, "|"를 통해 조건을 적용할 수도 있죠. 예시를 보겠습니다.

예시

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번과 같이 소괄호 앞뒤로 연결되는 글자를 추가하면 앞에서 공부했던 기능들이 똑같이 구현되는 것을 알 수 있죠! 어때요, 유용할 것 같죠?

10. quantifier(수량자) 활용하기

수량자는 한 글자가 몇 번이나 나타날지 구체화 시켜주는 역할을 하는데요, 이번에 알아볼 수량자는 "", "+", "?" 이 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[-@@][-@@]-
-> @@ @ 앞에 "@"를 제외한 다른 글자가 위치하면 선택

11. "{}"(curly brackets, 중괄호)로 수량 특정하기

선택하고자 하는 글자의 수량을 범위로 특정하려면 "{}"을 활용하면 됩니다. "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

12. 탐욕적 수량자(greedy quantifier)와 게으른 수량자(lazy quantifier)

수량자는 기본적으로 탐욕적(greedy)으로 설계되었다고 합니다. 여기서 탐욕적이라는게 무슨 뜻이냐면, 조건을 만족하는 가장 큰 덩어리를 찾는다는 뜻인데요, 예를 들면, 아래와 같은 상황을 들 수 있습니다.

정규표현식 : <div>.*</div>
match : [<div>what is</div><div>your name</div>]

위와 같은 경우, 두 개의 div 블록으로 나눠져야할 것 같지만, greedy한 수량자의 특성 때문에 가장 앞의

와 가장 뒤의
가 범위로 인지되어 하나의 블록으로 선택되게 됩니다. 이때 필요한 개념이 lazy한 수량자 입니다.

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 블록에서 먼저 조건이 만족되어 "?" 적용 전 처럼 전체가 선택되지 않습니다.

요 개념은 잘 살펴보지 않으면 나중에 고생할거 같으니... 여러번 봐두려구요.

13. "\w" 로 알파벳, 숫자 선택하기

\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)과 동일함

14. "\s"로 공백 표현하기

\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][.]

15. "\d"로 숫자 표현하기

\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)과 동일

16. "\b"로 단어 경계 표현하기

\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].

혹시 이해가 안가신다면, 위에서 경계를 도식화 해놓은 부분을 보고 다시 한 번 확인해 보시면 이해가 가실거에요!

17. "\A"와 "\Z"의 용법

\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]

이해가 되셨나요~? 되셨으면 좋겠네요!

18. "?=" 활용하기

"?="은 해당 패턴을 포함한 글자를 찾긴 찾는데, 찾은 값을 표기할 때는 해당 패턴을 표기하지 않는 방식입니다. 말로 하면 좀 어려운 것 같은데요, 예시를 들어서 설명해 볼게요.

예시

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"가 전부 선택되는걸 볼 수 있죠!

드디어 정리가 끝났네요! 생활코딩 강의 기반으로 이해안가는 부분은 추가로 확인해 가면서 글을 작성 해봤습니다. 글이 길어진 감이 있는데, 나중에 필요한 걸 찾을 때 여러 페이지로 나눠져 있으면 불편할 것 같아서, 좀 길더라도 글 하나 안에 다 담았습니다.

정리랍시고 하긴 했지만, 실 사용 예시들을 보면 여전히 이해 안가는 것들이 많아서... 사용해보다가 새로운 걸 알게 되면 다른 글로 또 정리해야겠습니다.

정규표현식이 어려우셨던 분들에게 조금이라도 도움이 됐길 바라면서... 그럼 이만...!

profile
https://velog.io/@jomminii 로 이동했습니다.

0개의 댓글