프날 오토핫키 강좌
누르면 강좌 리스트가 나와요
프날 오토핫키 강좌

⚠ 이 강좌는 오토핫키 v1을 다룹니다

지금 보시는 강좌는 구버전 오토핫키(v1.1)를 다루고 있습니다. 따라서 본 강좌의 내용은 현재 최신 오토핫키 버전 (v2.0)과 호환되지 않습니다. 구버전의 정보가 필요한 것이 아니라면, 가능한 한 새로운 사이트에 작성한 v2 강좌(https://ahkv2.pnal.dev)를 봐주시길 바랍니다.

[프날 오토핫키] 정규표현식(정규식) (중)

이번 강에선 실제로 RegExMatch()와 RegExReplace()를 써보고, 패턴 몇 개를 공부해보겠습니다.

 


 RegExMatch() 

RegExMatch(input, pattern [, outputVar])
  • input: 패턴이 찾아질 문자열입니다.
  • pattern: input 문자열에서 찾을 패턴입니다.
  • outputVar: 패턴 중 서브패턴(추후 설명)을 출력합니다.

매개변수는 위와 같으며, 반환값은 말씀드렸다싶이 찾은 패턴의 위치입니다.

 

즉, 이전의 예제였던 ["abc123"에서 "123"의 위치 찾기]를 하면, 

MsgBox, % RegExMatch("abc123", "123")

처럼 쓰면 되고, 4가 출력되겠지요.


 RegExReplace() 

RegExReplace(input, pattern [, replacement])
  • input: 패턴이 찾아질 문자열입니다.
  • pattern: input 문자열에서 찾을 패턴입니다.
  • replacement: 찾은 패턴을 치환할 문자입니다.

반환 값은 replacement로 치환된 input이 나올 것입니다.

 

이전의 예제였던 ["abc123"에서 "123""가나다"로 치환하기]를 해보면

MsgBox, % RegExReplace("abc123", "123", "가나다")

처럼 써주면 되며, "abc가나다"가 출력됩니다.


 패턴 문자의 종류 

 

중요한건 패턴입니다. 이 패턴에 따라서 문자열이 다르게 가공되기 때문입니다.

 

패턴을 알기 전에 "선행 문자" 개념을 알아야합니다. 선행 문자는 해당 패턴의 "바로 앞에 오는 문자"가 선행 문자가 됩니다. 예를 들어서, "abc"라는 패턴이 주어졌을 때 a는 b의 선행 문자이고, b는 c의 선행 문자입니다. (그렇다고, a가 c의 선행 문자인것은 아닙니다.)


1. Dot (.)

Dot 패턴 문자는 "한 개의 아무 글자"를 의미합니다. 한 글자가 어떤 글자가 되었든, 그 글자와 매칭됩니다.

var := "112233445566"
MsgBox, % RegExReplace(var, "3.5", "★")

위 정규식에서 패턴은 "3.5"입니다. Dot이 "3"과 "5"사이에 들어가있네요. 주어진 문자열은 "112233445566"입니다.

주어진 문자열에서 "3.5"패턴과 매칭되는 부분은 보이지 않습니다. Dot은 "한 개의 아무 글자"를 의미하기 때문에, 주어진 문자열에서 "3.5" 패턴과는 매칭되지 않습니다.

 

그러면, "3..5"패턴은 매칭될까요? 주어진 문자열에서 아무 두 글자가 3과 5사이에 들어있는 부분을 찾아보세요.

있습니다. 112233445566 처럼 일치하는 부분이 있습니다!

var := "112233445566"
MsgBox, % RegExReplace(var, "3..5", "★")

→ 결과 값으로 11223★566이 출력됩니다. Dot에 해당하는 부분이 아닌, 패턴 전체와 매칭이 되기 때문에 "3445"부분이 ★로 치환되었습니다.

2. Arsterisk / Star (*)

Star 패턴 문자는 선행 문자의 반복을 허용해줍니다. 예를 들어서, a*라는 패턴에서 *의 선행 문자는 a입니다.

그러면 a가 얼만큼 반복되어도, 그 모든 a와 매칭이 된다는 뜻입니다.

 

즉, a* 는 ab와 aaab와 aaaaaaab에서 모든 "a"와 매칭됩니다.

 

여기서 그 유명한 Dot-Star패턴이 나옵니다. (.*)

Dot은 "한 개의 아무 글자"이고, Star는 "앞 글자의 반복 허용"입니다. 즉, Star가 Dot의 "한 글자"라는 제한을 풀어준 것이지요. Dot-Star는 "아무 문자"와 같습니다.

 

var := "112233445566"
MsgBox, % RegExReplace(var, ".*445566", "★")

위 예제를 실행시켜보세요. Dot-Star패턴이 "아무 문자"와 매칭되기 때문에, "445566"앞에 있는 모든 문자열과 매칭하여 결국 "112233445566" 전체와 매칭이 되었습니다. 모든 문자열이 통째로 ★하나로 치환되겠네요.

Dot-Star 패턴이 위 예제에서는 "112233"과 매칭된 것을 볼 수 있습니다.


3. 물음표(?)

물음표 패턴 문자는 선행 문자를 "옵션화"시켜줍니다. 선행문자를 고려해도 매칭이 되고, 선행 문자를 고려하지 않아도 매칭이 됩니다.

"Colou?r"라는 패턴을 예로 들겠습니다. 물음표의 선행 문자는 u입니다. u가 옵션화 되었기 때문에 "있든 없든" 매칭될 것입니다. 즉, "Colou?r"라는 패턴은 "Color"와 "Colour"를 모두 매칭시키겠지요.

var := "color"
MsgBox, % RegExReplace(var, "colou?r", "★")

 

Dot-Star패턴에 물음표를 붙여주는 경우도 많습니다. 역시 선행 문자인 Star를 옵션화시켜주지만, 머리로 풀어나가기엔 복잡해지죠. Star는 선행자의 반복 허용인데, 선행자의 선행자의 반복을 허용하는 것을 옵션화 시켜주는 것이 (.*?)이니, 어려울 따름입니다.

 

그래서, Dot-Star 패턴 한정으로 ?는 아래처럼 생각하시면 쉽습니다.

"가장 짧은 크기의 문자열과 매칭"

가령, 123143이라는 문자열에서 "1.*3"을 매칭한다고 합시다. 다음과 같이 매칭되는 두 가지 경우의 수가 있겠지요.

123143
123143

Dot-Star의 범위만 칠해보면 아래와 같습니다.

123143
123143

기본적으로 패턴은 제일 넓은 범위를 매칭하게 됩니다. 즉, 123143전체를 매칭하게 됩니다. 이 때, 물음표를 Dot-Star뒤에 붙여주면 "123"만 매칭이 됩니다. 가장 작은 범위의 "1.*3"을 매칭한 것입니다.

 

이는 아래 코드로 확인해 볼 수 있습니다.

var := "123143"

MsgBox, % RegExReplace(var, "1.*3", "★",,1)
MsgBox, % RegExReplace(var, "1.*?3", "★",,1)

위는 .*로 써주었고, 아래는 .*?로 써주었습니다. 매칭된 부분은 별표로 치환됩니다. 뒤에 붙은 ,,1은 신경쓰지 말아주세요. 원래 RegExReplace()는 가능한 한 모든 경우의 수를 치환하는데, 이 횟수를 한 번으로 줄여준 것 뿐입니다.

 

위에는 .*를, 아래는 .*?를 써주었습니다. .*를 써주면 전체와 매칭되어서 별표 하나만 출력되며, .*?를 써주면 123만 매칭되어서 [★143]이 출력됩니다.


4. 소괄호 ( )

소괄호는 수학에서의 '우선 연산' 기능을 그대로 한다고 생각하시면 편합니다. (살짝 다르긴 합니다.)

즉, 여러개의 패턴 문자를 하나로 묶어서 한 덩어리로 취급할 수 있게 합니다. 이렇게 괄호로 묶은 한 덩어리를 "서브패턴"이라고 합니다.

 

예를 들어서, 선행 문자를 필요로 하는 패턴 문자 (예를 들면 Star나 물음표) 앞에 선행 문자로 서브패턴이 있다면, 해당 서브패턴 전체가 선행 문자가 됩니다.

 

가독성을 위해 써주셔도 좋고요, 자유롭게 쓰시면 되나 그렇다고 막 쓰시면 안됩니다. 의미없는 서브패턴을 만들면 오히려 복잡해보이기 때문입니다.

var := "동해물과 백두산이 마르고 닳도록"
RegExMatch(var, "동해물과(.*)닳도록", output)
MsgBox, % output1

→ RegExMatch의 세 번째 매개변수는 서브패턴이 매칭되는 부분을 pseudo-array에 담아줍니다.
pseudo-array란, 변수명 뒤에 1, 2, 3등의 숫자를 붙여서 배열을 만드는 식입니다. 첫 번째 서브패턴의 값은 output1에 담기겠네요.

→ RegExMatch(var, "동해물과(.*)마르고(.*)", out) 처럼 썼을 경우엔 out1엔 "백두산이"가, out2 변수엔 "닳도록"이 담길 것입니다.

 


5. Bar ( | )

Bar 문자는 or(또는)의 기능을 합니다. 범위 제한을 위해 서브패턴 안에서 써주는 경우가 많습니다.

예를 들어서, gr(e|a)y라는 패턴이 있다면 이 패턴은 gray와 grey라는 문자열 모두랑 매칭됩니다.

MsgBox, % RegExReplace("greygray", "gr(e|a)y", "★")

위 예제는 grey와 gray 모두에게 매칭되어서 별 두개가 출력될 것입니다.

 

var := "Autohotkey(AHK) is scripting language"

MsgBox, % RegExReplace(var, "(Autohotkey|AHK)", "Python")

위 예제처럼, 긴 단어 또한 서브패턴 안에 Bar로 구분해서 패턴을 쓸 수 있습니다. 이 예제에선 Autohotkey와 AHK라는 문자열 모두가 매칭되기 때문에, Python(Python) is scripting language라는 문장이 출력되겠네요.

 


6. 대괄호 ([ ]), Carrot (^)

대괄호는 하이픈(-)을 이용하여 특정 문자의 범위를 유니코드를 기준으로 지정해줍니다. 예를 들어서, 아래와 같은 식입니다.

MsgBox, % RegExReplace("123ABC", "[0-9]", "Z")

[0-9]라고 쓴 패턴이 보이는데, 이는 0부터 9까지를 매칭한다는 뜻입니다. 주어진 문자열 "123ABC"에서 123이 매칭되어서 ZZZABC라는 출력을 만들겠네요.

 

여는 대괄호 뒤에 Carrot을 붙여주어서 부정을 나타낼 수도 있습니다.

MsgBox, % RegExReplace("123ABC", "[^0-9]", "Z")

[^0-9] 패턴에 의해서, 0부터 9까지를 제외한 모든 문자가 매칭됩니다. ABC가 매칭되어서 123ZZZ라는 문자열이 출력될 것입니다.

 

여러개의 범위를 설정해주실려면 대괄호를 닫지 않고 한 대괄호 안에 하이픈으로 구분하여서 써주시면 됩니다.

MsgBox, % RegExReplace("123ABC가나다", "[0-9가-힣]", "Z")

위 패턴은 0부터 9까지, "가"부터 "힣"까지를 Z로 치환하겠네요. (출력: ZZZABCZZZ)

한글 유니코드는 "가"부터 시작하여 "힣"에서 끝납니다. 영어는 소문자 따로, 대문자 따로 해서 a부터 z까지 한번, A부터 Z까지 한번 써주시면 됩니다. ([a-zA-Z])

유니코드 범위에 따라서 지정해주는 것이기 때문에, 꼭 영어나 숫자가 아니더라도 충분히 사용 가능합니다. 특수문자도 되고요, 자세한건 유니코드 표를 참고하시면 좋을 것 같습니다.

 


 이스케이프 시퀀스 

강좌 본편에서 이스케이프 시퀀스에 대해 다룬 적이 있습니다. `n `, 처럼 이스케이프 문자(`)를 앞에 붙여 주는 식이었습니다.

정규식에도 이스케이프 시퀀스가 있습니다. 여기선 이스케이프 문자가 역슬래시(\)이기 때문에, \를 이용하여 구성해주어야합니다.

 

이스케이프 시퀀스라 해봤자 "특정한 기능을 하는"건 없고요(그게 곧 패턴 문자니까요)

패턴 문자를 날 것 그대로 출력하기 위해 필요합니다. 예를 들어서, 패턴 문자로서의 "?"가 아닌 일반 문자열로서의 "?"를 써야할 때 말입니다.

 

이렇게 예외 처리를 해주어야하는 문자열은 \.*?+[{|()^$ 이렇게 해서 총12가지입니다.

예를 들어서 괄호 안의 내용을 매칭 시키고 싶으면

((.*))

가 아니라

\((.*)\)

로 써야겠지요. 이스케이프 시퀀스 처리를 안하면 소괄호는 서브패턴으로 인식 될 것입니다.


모든 패턴 문자를 다루지는 않았지만, 가장 자주 쓰이는 일부 패턴 문자를 다루어보았습니다. Dot, Star, 물음표, 소괄호, 대괄호, Bar, Carrot 이렇게 7가지 패턴 문자만 가지고도 대부분의 정규식은 작성할 수 있습니다.

 

패턴과 매칭되는 부분을 입력 문자열에서 찾아주는 정규식을 잘 이용하면 복잡한 파싱 과정을 한 줄로 끝낼 수 있습니다.

 

다음 강에선 정규식을 이용하여 파싱을 직접 해볼 수 있도록 여러 문제를 준비했습니다.

직접 컴퓨터로 풀어보세요.