정규 표현식 강좌(Autohotkey)

정규 표현식

위키백과, 우리 모두의 백과사전.

정규 표현식
(正規表現式, Regular expression에서 줄여서 Regexp 또는 Regex)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다. 정규 표현식은 많은 텍스트 편집기프로그래밍 언어에서 문자열의 검색과 치환을 위해 지원하고 있으며, 특히 Tcl은 언어 자체에 강력한 정규 표현식 구현을 내장하고 있다.

정규 표현식은 컴퓨터 과학의 정규 언어로부터 유래하였으나 구현체에 따라서 정규 언어보다 더 넓은 언어를 표현할 수 있는 경우도 있으며, 심지어 정규 표현식 자체의 문법도 여러 가지 존재하고 있다. 이 중 표준화된 것으로는 POSIX의 확장 정규 표현식이 있으며, 표준화되지는 않았지만 펄의 정규 표현식과 그 대체 구현인 PCRE도 널리 사용된다.

문법

몇몇 문자는 특정한 논리적 관계를 나타내는 기호로 쓰인다. 이를 제외한 나머지 문자는 일반적인 문자를 나타낸다.

  • 선택 기호: "|" 기호는 여러 식 중에서 하나를 선택한다. 예를 들어, "abc|adc"는 abc라는 문자열과 adc라는 문자열을 모두 포함한다.
  • 묶기 기호: "("와 ")"로 여러 식을 하나로 묶을 수 있다. "abc|adc"와 "a(b|d)c"는 같은 의미를 가진다.
  • 개수 기호: 문자 뒤에 붙어 문자의 개수를 나타낸다.
    • "*" : 0개 이상. "a*b"는 "b", "ab", "aab", "aaab"를 포함한다.
    • "+" : 1개 이상. "a+b"는 "ab", "aab", "aaab"를 포함하지만 "b"는 포함하지 않는다.
    • "?" : 0개 또는 1개. "a?b"는 "b", "ab"를 포함한다.
    • "{m, n}" : m개 이상 n개 이하. "a{1,3}b"는 "ab", "aab", "aaab"를 포함하지만, "b"나 "aaaab"는 포함하지 않는다.

이에 따라 "(fa|mo|b?o)ther"는 "father", "mother", "bother", "other"를 나타낸다.

많은 프로그래밍 언어에서는 이를 확장한 문법을 가지고 있다. 이 중 일반적으로 사용되는 연산자는 다음과 같다.

  • "[]" : "["과 "]" 사이의 문자 중 하나를 선택한다. "|"를 여러 개 쓴 것과 같은 의미를 가진다. 예를 들면 [abc]d는 ad, bd, cd를 뜻한다. 또한, "-" 기호와 함께 쓰면 문자의 범위를 지정할 수 있다. "[a-z]"는 a부터 z까지 중 하나, "[1-9]"는 1부터 9까지 중의 하나를 뜻한다.
  • "[^]" : "[^"과 "]" 사이의 문자를 제외한 나머지 하나를 선택한다. 예를 들면 [^abc]d는 ad, bd, cd는 포함하지 않고 ed, fd 등을 포함한다. [^a-z]는 알파벳 소문자로 시작하지 않는 모든 문자를 나타낸다.
  • "^", "$" : 각각 문자열의 처음과 끝을 나타낸다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

위키피디아에서는 위와 같이 설명하고 있습니다.
AutoHotkey의 정규 표현은, Perl 5의 정규 표현식과 그 대체 구현인 PCRE(Perl Compatible Regular Expressions)라고 하는군요.

정규식은 우리나라엔 없는 단어로 정규 표현식이라고도 하며, 영어의 regular expression를 번역해 놓은 것입니다.
텍스트 속에서 일정한 패턴을 찾아 컴퓨터가 알아듣기 쉽도록 수식화 해놓은 것이라 쉽게 이해하시면 됩니다.

예를 들어, "86년 아시아 게임, 88년 서울 올림픽, 98년 스타크래프트 대유행, 2011년 대찬인생 오핫사사 가입" 이라는 텍스트가 있다고 가정합니다.

여기서 볼수 있는 패턴은 무엇일까요? 86년, 88년, 98년이 연도를 표현하는 것이라는 것을 알 수 있습니다.
우리는 쉽게 알 수 있지만 컴퓨터는 어떻게 이것을 알아볼 수 있을까요?
컴퓨터에게 이것을 알려주기 위해 사용하는 것이 바로 "정규식" 입니다.

우리는 이것을 직접 상수를 찾아 일일이 변경해 줄 수 있습니다.
예) 찾기: 86년 -> 바꾸기: 1986년

이런것이 한줄이면 쉽겠지만 수백 수천줄이라면 어떻게 할까요? 86, 88, 98을 변수처럼 찾고 바꾸면 되지 않을까요?
AHK에서 그것을 가능하게 하는 명령어는 RegExMatch(), RegExReplace() 가 있습니다. 정규식을 이용한 검색과 치환은 이것으로 가능해 집니다. 자세한 것은 첨부파일의 레퍼런스를 참고 하시면 되겠습니다.

정규식으로 숫자두개+년으로 구성된 단어를 찾는것은 어떻게 할까요?


백번 듣는것 보다 한 번 해보는 것이 낫겠지요. 간단한 실습용 Gui를 작성하려고 했더니 이역시 이미 있더군요.
역시 누구도 상상하지 못한 새로운 것을 생각해 낸다는 것은 어려운 일인가 봅니다.
구글에서 검색하실 때 DotStar site:www.autohotkey.com 이런식으로 검색하시면 해당 사이트 내의 관련 단어만 검색해주니 관심 있으신 분은 직접 검색해보시면 되겠습니다. RegEx Tester라는 것도 있으니 우선은 입맛에 맞으시는 걸 찾으시길 바랍니다. DotStar는 첨부해 두었습니다.

위 그림에서 \b([0-9][0-9])년 이라고 쓴 곳이 정규식을 사용한 곳입니다.
[0-9]는 숫자를 찾으라는 것이고 \d라고 사용할 수도 있습니다. 두번 들어갔으니 두자리 숫자와 년이라는 글자가 합쳐진 패턴을 찾게 되겠네요.

그런데 2011년에도 11년이라는 패턴이 들어갑니다. 우리는 두자리 숫자에 19를 더해 1986년과 같은 형식을 만들고 싶은데 201911년이 되어 버리면 안되겠지요? 그래서 \b 라는 단어 구분자가 앞에 붙었습니다.
다르게는 \b(\d{2})\W. 라고 해도 \b([0-9][0-9])년 과 같은 결과를 얻을 수 있습니다.

여기서 설명하는 것들을 당장에 모두 이해하지 않아도 됩니다.
정규식에는 정답이 없으며 기본적인 것들만 익히면 누구나 어렵고 복잡한 수식을 만들어낼 수 있고 원하는 것을 검색하고 치환할 수 있습니다. 그것은 개인의 상상력에 달린 것이며 여러분의 상상력에 날개를 달아드리는 것이 이 글의 목적 입니다.

이미 다 이해하고 계신분들, 이 글을 보면서 이해하신 분들, 전혀 감이 안오시는 분들이 있을겁니다.
제가 아는 수준에서 최대한 쉽게 설명해 드릴테니 필요하신 분은 준비물(실습용 테스터와 레퍼런스)과 의지만 챙기셔서 따라오시면 됩니다.

http://www.autohotkey.pe.kr/bbs/board.php?bo_table=script&wr_id=300&page=9
이곳에 숫자를 어떻게 1000자리로 끊어 내느냐에 대한 여러 유저분들의 지난 고민들이 있습니다.
알아볼 수 없는 문자열의 조합들. 우리는 그런 것들이 어떻게 만들어지는가에 대한 근본적인 고민을 할 것이며 이야기가 끝날 때 쯤엔 위 링크의 내용을 이해하고 정규식을 마음대로 응용하게 되는 정도의 수준을 목표로 합니다.

그럼 뒷 이야기는 시간이 나는대로 계속 이어가겠습니다.

 

 

이 글의 목적은 어떤 방법으로 우리가 원하는 글자만 인식할 수 있는가와 그에 필요한 명령어 습득에 있습니다.
따라서 정규식과 관련없는 부분에 대해서는 자세하게 설명하지 않으니 해당 부분은 검색을 통하거나 스스로 공부를 해서 익히시기 바랍니다.

http://www.technote.co.kr/php/technote1/board.php?board=community&command=body&no=2892
국내의 테크노트라는 홈페이지 제작 프로그램이 중국 연길에 도용당하는 사건이 발생하였습니다.

http://media.zoglo.net/board.php?act=view&board=news_renwu&no=958
이에 격분한 대한민국 네티즌들은 무궁화소프트웨어라는 곳의 기사를 찾아냅니다.

분명히 저작권 위반이지만 국제법에 대해서는 잘 모르겠고 ahk를 이용해 댓글도배나 했으면 좋겠네요.
(공부를 재미있게 하기 위한 가상의 스토리일뿐 실제로 테러를 하려는 것은 아닙니다.)

그런데 이상한 것이 있습니다.

여기다가 fa101을 입력 해야 글이 등록될 것 같군요.
이미지서치로 찾으려면 빨간색 a-z, 0-9를 모두 저장해서 일일이 검색하는 귀찮은 작업을 해야겠군요.
그런데 우리의 ahk는 웹페이지의 소스를 변수에 저장할 수 있습니다.

<font style=color:red;font-size:11pt;font-family:굴림;><b>f</b></font>a3<font style=color:red;font-size:11pt;font-family:굴림;><b>a</b></font><font style=color:red;font-size:11pt;font-family:굴림;><b>1</b></font><font style=color:red;font-size:11pt;font-family:굴림;><b>0</b></font><font style=color:red;font-size:11pt;font-family:굴림;><b>1</b></font>431 &nbsp; <input type="text" name="_block" class="input" size="10"> &nbsp;<font color=#999999>붉은글만 순서대로 입력하십시요。</font>
해당 페이지의 소스를 보면 위와 같은 부분이 바로 해당 글자를 화면에 보여주는 부분 입니다.
red;font-size:11pt;font-family:굴림;><b></b>사이에 있는 글자만 추출해서 모으면 fa101이 나오겠군요.
이 정도는 다른 분들이 이미 수차례 올려놓은 기간제한 등 다양한 스크립트를 조금만 응용하면 쉽겠습니다.

우리는 정규식을 공부하고 있으니 정규식을 이용해서 찾아봅시다.
(red;font-size:11pt;font-family:굴림;><b>)(\w)
DotStar을 이용해 이렇게 찾으면 결과가 아래와 같이 나옵니다.
1: red;font-size:11pt;font-family:굴림;><b>
2: f


()로 둘러싸면 그룹화라고 레퍼런스에 나와있습니다. 앞에것부터 순서대로 1, 2가 붙습니다. 두번째에 그룹화 한 부분이 \w기 때문에 우리는 2의 내용만 뽑아내면 되겠네요.

\(backslash)의 이름은 영어로 escape character 입니다. 말그대로 어떤 문자든지 고유의 의미에서 탈출시켜주는(escape) 기능을 합니다.
한글 자판에서는 원화표시로 나오지만 사실은 / (slash)와 반대방향으로 돼 있는 모습이고 그래서 slash의 반대인 backslash라고 부릅니다. 한글 윈도우에서만 원화표시로 보일뿐 외국인들은 백슬래시의 원 모습으로 사용하고 있습니다. w는 한개의 문자일 뿐이지만 \가 붙으면 아래와 같이 특별한 의미를 갖게 됩니다.

\w - 영문알파벳과 숫자를 포함한 일반문자. [a-zA-Z0-9]와 같습니다. 한글은 문자로 취급하지 않습니다.

\W - 영문알파벳과 숫자를 제외한 모든 문자. 여기서 W는 WhiteSpace의 약자로 서식의 의미를 의미를 갖는 공백, 탭, 줄바꿈문자 등등을 모두 포함.  [^a-zA-Z0-9] 또는 [^\w]와 같습니다. 한글도 여기 포함됩니다.

그런데 (red;font-size:11pt;font-family:굴림;><b>)(\w) 라고 쓸 거면 정규식을 사용하지 않는것과 별 차이가 없겠네요.
프로그래머들은 최대한 스크립트를 줄이려고 노력합니다. 정규식이라는 것도 사실은 그 노력의 결과물로 탄생한 것이라 할 수 있습니다. 그럼 우리도 해봐야겠네요.

찾기 : red.{37}(\w)
결과 : 1: f

아주 간단해졌습니다. 앞에서는 ()를 설명하기 위해 두번 사용했지만 위와 같이 필요한 부분에만 사용하면 됩니다.

. - 줄바꿈 문자 이외의 어떤 문자와도 일치합니다. a. 라고 하면 a로 시작해서 어떤
문자든 문자 하나가 따라오면 조건을 만족한다는 말입니다.

{n} - n회라고 레퍼런스에 나와 있습니다. {}안에 쓰여진 숫자만큼 패턴을 반복하게 됩니다.

red 다음에 .{37} 이라고 적었으니 red 다음에 아무 문자라도 37자가 오면 조건을 만족하게 됩니다.
(여기서 왜 35자가 아니냐고 질문을 하실 직접 글자를 세어보신 분이 계신다면, 영어가 1이라면 한글은 2입니다.)
해당 페이지에는 red라는 텍스트가 해당 부분 외에는 없으므로 위와 같이 간단히 찾을 수 있습니다.
하지만 red라는 텍스트가 여러부분에 걸쳐서 나올 수도 있고 이 외에 다른 방법으로도 얼마든지 찾을 수 있습니다.
이런 이유로 정규식에는 정답이 없게 되는 것이기도 하지요. 정규식의 한계는 상상력의 한계와 일치합니다.
제가 설명하지 않는 부분들은 각자 공부하셔서 댓글로 남기시거나 다른 분들과 의논하시기 바랍니다.

RegExMatch를 이용해서 잘 찾아지는지 확인하는 스크립트 예제 입니다.
fileread,test,test.txt    ; test.txt는 해당 홈페이지의 소스가 담긴 텍스트 파일. 파일을 읽어서 test라는 변수에 저장합니다.
pos = 1
While pos := RegExMatch(test, "red.{37}(.)", abc, pos+StrLen(abc))  ; test 변수에 담긴 내용에 정규식을 적용후 abc라는 변수에 넣습니다.
msgbox, %abc1%     ; abc는 찾은 줄을 모두 표시하고, abc1이라 하면 ()로 묶어준 그룹 중에 1번 내용만 표시합니다.
return


눈으로만 보지 마시고 직접 실행 해보시기 바랍니다. 모든 명령어를 다 욀 필요는 없지만 기본적인 이해만 하면 레퍼런스만 보면서 온갖 정규식을 다 만들어낼 수 있습니다.
Captcha를 긁어오기는 성공했으므로 이번 강좌는 여기서 마치고 다음에 계속 이어가겠습니다.

 

 이글은 autohotkey.pe.kr의 '대찬인생'님의 강좌 입니다.

'Study > AutoHotKey' 카테고리의 다른 글

특정 창에서만 단축키가 작동하도록 하는 방법  (0) 2011.12.07
한줄씩 입력 받기  (0) 2011.10.20
한영 전환키 check  (0) 2011.10.18
Script> 좌표/색 도우미  (2) 2011.10.17
AHK 자주사용하는 명령어  (0) 2011.10.17
내장변수  (0) 2011.10.10
키보드 스캔코드  (0) 2011.10.10
Gui는 어렵다?  (0) 2011.10.09
정규 표현식 설명(예제포함)  (0) 2011.10.09
정규 표현식  (0) 2011.10.09