정규식 Regular Expression 기초 3/3

Posted by Myoa™
2016. 9. 12. 23:53 개발/Python
본 블로그는 클리어타입에 최적화 되있습니다. 글씨가 흐리게 보이면 여기를 눌러 패치하세요(1차수정)


정규식 Regular Expression 기초 3/3


지금까지 기본적인 아무개문자, 문자집합, 연산자, 수량자를 배웠습니다.

이번 강의를 끝으로 사전정의된 문자집합, 하위식과 하위식보조표현,

그리고 부록으로 정규식의 플래그에 대해서 알아보도록 하겠습니다.

아래 나올 사전정의된 문자집합들은 반드시 외우시기 바랍니다. 외워야만 하는 부분입니다.



18.

\w는 문자“w”를 이스케이핑하여 “w”가 아닌 [A-Za-z0-9_]를 의미합니다.

당연하게도 이 문자집합에 대해서도 수량자를 적용할 수 있습니다.


19.

반대로 \W는 \w를 제외한 모든 문자에 해당됩니다.

\w는 [a-zA-Z0-9_] 라면 \W는 [^a-zA-Z0-9_]입니다.

따라서 영소대문자, 숫자, 언더스코어( “_” )를 제외하고 찾게됩니다.



20.

\s는 “s”를 이스케이핑하여 공백 문자를 찾게해줍니다. 여기서 공백은 “ ”만 의미하는 것이 아니라 “\n” 뉴라인도 포함합니다. 당연히 “\t” 탭도 공백이므로 탭문자도 포함합니다.

반대로 \S는 공백을 제외한 모든 문자를 찾습니다.



21.

\d는 Numeric을 찾습니다. 문자집합으로 표현하면 [0-9]입니다.

\D는 Numeric을 제외한 문자를 찾습니다. 문자집합으로 표현하면 [^0-9]입니다.



22.


\b는 문자의 경계선을 찾습니다. 이해하기 약간 모호할 수 있습니다.

 The best plan is no plan.

이해를 돕기위해 음영처리했습니다.

\b를 검색해볼까요?

이런, Infinite Exception이 발생합니다. 이는 \b단일 식이 순환참조를 유발시키기 때문입니다.

어디 단어에서부터의 경계인지가 모호하기 때문에 정확한 판단을 내릴 수 없고 무한루프에 빠지게 되죠.

\b.+\b 식으로 검색해보겠습니다.

[“The”, “ ”, “best”, “ ”, “plan”, “ ”, “is”, “ ”, “no”, “ ”, “plan”] 이 매치됩니다.


“ The”에서 단어의 경계는 “ ”도 아니고 “T”도 아닙니다. 그 사이가 경계죠. 굳이 표현하자면 “ |T” 저 부분이 됩니다.

따라서 \b는 저 경계를 찾는 것입니다.

\b.+\b 식은 ‘경계와 하나이상의 아무문자와 경계’ 를 찾습니다.

저번에 제가 .은 공백(“ ”)도 찾는다고 말씀드렸습니다.

그래서 “ ”도 매치가 되는겁니다.

“The| |best”

“best| |plan” 이 부분도 식에 부합하기 때문이죠.



\B는 \b의 반대를 찾습니다. 따라서 단어와 단어사이의 경계가 아닌부분 즉 문자와 문자사이의 경계를 찾습니다.

“The best plan is no plan”라는 문장에서는

“T|h|e b|e|s|t p|l|a|n i|s n|o p|l|a|n”

\B.\B 식은

[“h”, “e”, “s”, “l”, “a”, “l”, “a”] 만 매치될겁니다.

is no에서 i와 s사이는 분명 \B가 맞습니다. 하지만 o뒤는 문자와 문자사이의 경계가아니라 단어와 단어사이의 경계가 되죠. 따라서 식에 부합하지 않습니다.

실제로도 다음과같이 매치됩니다.


24.

24번은 통용되는 정규식이 아니기 때문에 제외하겠습니다.




25. 26.


25번을 진행하기전에 하위식과 그 연산들에 대해 알아보겠습니다. 여기서부터는 굉장히 고차원적인 내용이 나옵니다.

여러본 곱씹어보면서 읽어보세요.


하위식은 정말 간단합니다. 괄호안에 식을 넣어주면됩니다. /(이렇게요)/g

이제 매치와 그룹의 차이점을 알려드릴텐데

서브넷마스크 B클래스에 해당하는 “255.255.0.0”를 가지고 지지고 볶아보겠습니다.

각 옥텟에 해당하는 숫자만 가져오는걸 목표로 하겠습니다. (우리는 IPv4의 주소형식에 일치하는지를 보는 것이 아니라 정상적인 IP주소를 기준으로 각 옥텟의 숫자를 가져오는 것이 목표입니다)

우리가 배운바로는 저 IP주소를 정규식으로 잡아보기위해

\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} 로 작성하게됩니다.

[“255.255.0.0”] 이 잡히죠.

하지만 우리가 원하는건 각 옥텟의 숫자입니다.

이럴경우에는 subexpression을 이용해서 식은 전체를 매치시키되, 원하는 값만 그룹화합니다.


(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}) 식을 이용하면

[“255.255.0.0”] 이 매치되고

[“255”, “255”, “0”, “0”] 로 그룹화됩니다.

매치는 말 그대로 식전체에 부합하는 매치결과를 반환하고, 그룹은 하위식이 있을 경우 하위식의 내용들만 추출해서 그룹화하여 반환합니다.


하지만 하위식을 사용하다보면 몇몇 문제를 마주칩니다. 자주 사용되어지는 상황들에 대해 기술해보겠습니다.


1) ?:

OR연산을 예시로들자면

“The chair is under the desk.” 라는 문장에서 의자가 어디에 있는지를 찾아내려고 합니다.

(on|in|under).+ 의 식을 이용하고자 합니다.

[“under the desk”] 가 매치되고 [“under”] 가 그룹으로 반환됩니다.

우리는 의자가 어디에 있는지를 알고싶은것이지, 굳이 전치사를 그룹으로 얻고 싶지 않습니다.

이럴 때 하위식 맨앞에 “?:”를 넣어줍니다.

(?:on|in|under).+

이 경우 [“under the desk”] 가 매치되고 그룹되는 것은 없습니다.


2) ?=

전위연산의 긍정형입니다.

전위연산이란, 전위연산식 앞에 일치하는 패턴을 찾는것으로써

위 예시를 보면

\w+(?=X) 라는 식으로 [“AAA”]가 매치되었습니다.

“AAAX—AXXAAA-AbcdxX” 라는 문장에서는

[“AAA”, “AX”, “Abcdx”] 가 매치됩니다. 3개의 요소 전부 뒤에 “X”가 붙는다는 공통점이있죠.

하지만 매치되지는않습니다.

이러한 경우를 단순히 Non-Capturing이 아니라, Consume하지 않는다고 합니다.

저도 정확하게 이해한 개념이 아니기 때문에 섣불리 단정지을 수는 없지만 위에 \b처럼 해당 문자를 캡쳐하지않고 경계를 나타낸다고 보는게 좋습니다.

AAA|X 다음과 같이요.

(\w+)X 와는 다른 결과죠. AAAX까지 매치가 되고, AAA가 그룹으로 반환됩니다. X를 소비한셈이 되죠.



3) ?!

전위연산의 부정형

?= 전위연산의 부정형입니다.

위에서는 해당 식이 있으면 검색이지만 부정형은 없어야만 가능합니다.

그래서 단일 문자보다는 문자집합이나 OR연산을 통해 필터링처럼 사용됩니다.


4) ?<=

후위연산의 긍정형

전위연산이 Capture(lookahead)였다면

후위연산은 

(lookabehind)Capture입니다.

따라서 (?<=X)\w+

“XAAA—BXADEF—POWERX” 라는 문장에서

[“AAA”, “ADEF”]가 매치됩니다.

주의할점은 위에도 적었듯이, 후위연산식 뒤에 캡쳐할 식을 작성하셔야합니다.


5) ?<!

후위연산의 부정형

후위연산역시 부정형이 존재합니다.

작동은 방식은 전위연산의 부정형과 같습니다.


※ 단, 후위연산은 JS에서 공식적으로 지원하지않습니다. 백엔드에서 사용되는 언어들 (python, Java, etc...)의 Regex모듈은 지원합니다.



정규식 플래그 (Regular Expression Flags)

(http://regexr.com 에서 발췌)


I

Makes the whole expression case-insensitive. For example, /aBc/i would match AbC.

1강에서 배웠듯이 정규식은 기본적으로 case sensitive(대소문자 구별)합니다. 하지만 i플래그를 주면 case-insensitive되어 대소문자구별없이 검색하게됩니다.

ex)“Power! power! POWER!”라는 문장에서

/power/g

>> [“power”]

/poWER/gi

>> [“Power”, “power”, “POWER”]


g

Retain the index of the last match, allowing subsequent searches to start from the end of the previous match.

Without the global flag, subsequent searches will return the same match.


RegExr only searches for a single match when the global flag is disabled to avoid infinite match errors.


마지막 매치가 끝날때까지 계속 해서 검색을합니다. g플래그가 없을경우에는 한번이라도 매치되는 결과가 나온다면 검색이 종료됩니다.

ex) “과일은 우리에게 많은 영양소를 제공합니다. 과일섭취를 늘립시다.”라는 문장에서

/과일.+/g

>> [“과일은”, “과일섭취를”]

/과일.+/

>> [“과일은”]



m

When the multiline flag is enabled, beginning and end anchors (^ and $) will match the start and end of a line, instead of the start and end of the whole string.

Note that patterns such as /^[\s\S]+$/m may return matches that span multiple lines because e the anchors will match the start/end of any line.

^ $는 “첫”문장의 시작부터 “마지막”문장의 끝까지를 판단합니다. 하지만 m 플래그를 사용한다면 그 문장의 범위가 각 문장을 좁혀져 판단합니다.


ex) 


/^[\w\W]+?$/g 식은 전체를 한문장으로 판단하여 전체가 매치되는 반면,

/^[\w\W]$+?/gm 식은 각 문장의 처음과 끝사이의 모든([\w\W]+?)문자를 가져오라는 의미이므로

각 문장, 즉 총 11줄을 매치하고 그룹화합니다.



이렇게 지금까지 3장에 걸쳐서 정규식 Regular Expression의 기초를 배워봤습니다.


수고많으셨습니다.