정규표현식

1 개요

正規表現式 / Regular Expression

정규식이라고도 부른다. 보통 regex 혹은 regexp 라 많이 쓴다.

2 설명

프로그래밍에서 사용하는 일종의 형식 언어. 주로 문자열(string) 관련 프로그래밍에 많이 사용된다. 특히 컴파일러의 파서 부분은 이 정규표현식이 반드시 들어간다. 프로그램을 작성할 때는 특성상 일정한 규칙을 가진 텍스트 문자열을 사용하는 경우가 많은데, 이럴 때 정규 표현식을 사용한다. 유닉스 계열 운영체제에서 CLI 환경을 주로 사용하는 경우 grep, sed, awk 등으로 인해 거의 필수적으로 알게될수밖에 없는 언어.(모르고 CLI 환경에서 컴퓨터를 사용할수도 있긴 하지만, 그럴경우 그냥 GUI 환경을 사용하는것에 비해 노가다가 현저하게 늘어나고 생산성이 매우 떨어진다.)

잘 알아두면 문자열을 다루는 데 대단히 유용하기 때문에 C를 비롯한 여러 프로그래밍 언어에서 이 정규 표현식을 지원한다. 특히 웹 프로그래밍은 문자열을 다루는 빈도가 특히 높기 때문에 사용하는게 거의 필수적. 예를 들면 위키위키만 해도 DB에 저장된 텍스트에 있는 위키 문법을 엔진에서 해석해서 출력해 주는 작업이 필요한데, 위키 문법도 일정한 규칙이 있는 문자열인 만큼 이 작업에서 정규표현식은 반드시 들어간다.

그냥 읽기에는 상당히 난해한데, 문법이 일반적인 프로그래밍 언어의 형식과는 판이하게 다른데다 한 눈에 알아보기 어렵게 되어 있다. 정규표현식 중간에는 강제개행이 허용되지 않기 때문에 한 줄에 주르르륵 쓰게 되는데, 조금만 복잡해지면 각종 특수문자가 마구마구 튀어나오기 때문에 무슨 외계어처럼 느껴진다. 이 때문에 초보 프로그래머는 가급적 이 물건에 손을 안 대려고 하는 경향이 많은데, 정규식을 안 쓸 경우 문자열 처리 코드 자체가 대단히 복잡해진다. 사실 정규식이 복잡한 것은 확실한 단점이기 때문에 문자열 처리 함수를 다양하게 제공하는 언어도 있지만, 문자열이라는 것이 항상 고정된 형식으로 나오는 것이 아니기 때문에 정규식을 안 쓸래야 안 쓸수가 없다.

정규 표현식은 언어마다 문법이 조금씩 다른데, 쉘에서 주로 정규표현식을 사용하는 사용자 입장에서는 이것이 문제가 되는경우도 종종 있다. 크게 나누면 표준으로 인정된 POSIX의 정규 표현식과 그것에서 문법을 매우 확장한 Perl 방식의 PCRE[1] 이 둘로 나뉘어지며, POSIX 표준의 경우 다시 Basic 과 Extended 로 나뉜다. 이외에도 Emacs 와 Vim 모두 자체적인 정규표현식을 지원한다. 문제는, 이 정규표현식들이 완전히 다르면 모르겠지만, 비슷하면서도 살짝살짝 다르기때문에 그 차이들을 다 외우고 있을수도 없는 노릇이며, 정규표현식 자체가 다량의 텍스트를 다루는 명령인만큼 작은 실수가 커다란 차이를 불러올 수 있다는점이다. 즉, 쉘에서 쓰기 작업에 정규표현식을 동반할경우 매우 주의를 요할 필요가 있다. 또다른 문제는, 자신이 뭘 원한다고 해서 그것만 배우고 쓰면 되는게 아니라 프로그램마다 지원하는 정규표현식이 다르기때문에 그에 맞춰 배우고 써줘야 한다.[2] PCRE의 경우 이미 정규표현식이라고 부를 수 없을 정도로 기능이 확장되어 있는데[3], 덕분에 이런 이상한 짓도 가능하다. (...)

3 문법

문자열에서 URL을 찾는 정규표현식의 예제는 다음과 같다.

/(http|https|ftp|telnet|news|mms)://[^\"'\s()]+/i

위 정규식은 아래와 같이 구분이 된다.

/(http|https|ftp|telnet|news|mms)://[^\"'\s()]+/i
패턴구분자 시작찾을 문자열의 패턴패턴구분자 끝패턴변경자

3.1 패턴구분자

정규식의 패턴이 달라질 경우 그것을 구분하는 문자로, 정규식 패턴이 하나만 있을 경우에는 굳이 쓸 필요가 없지만 대부분 붙인다. 난해하게 만드는 주범이다. 왜냐하면, 이 패턴구분자는 특수문자 중 역슬래쉬(\\)를 제외하고 아무거나 쓸 수 있기 때문이다. 심지어 아래 등장하는 의미를 가진 문자(메타 문자라고 부른다)도 패턴구분자로 쓸 수 있다. 보통 슬래쉬(/)를 많이 사용하지만 규칙이 있는 것이 아니라서 정말로 프로그래밍 하는 사람 마음대로다. 이래저래 헷갈리는 주범 중 하나.

3.2 메타 문자

정규표현식에서 일정한 의미를 가지고 쓰는 특수문자를 메타 문자라고 부른다.

  • ^ : 문자열의 시작
  • $ : 문자열의 종료. 옵션에 따라 문장의 끝 또는 문서의 끝에 매치된다.
  • . : 임의의 한 문자
  • []: 문자 클래스. 문자 클래스 안에 들어가 있는 문자는 그 바깥에서 하나의 문자로 취급된다.
  • | : or를 나타냄
  • ? : 앞 문자가 없거나 하나 있음
  • + : 앞 문자가 하나 이상임
  • * : 앞 문자가 0개 이상임
  • {n,m}  : 앞 문자가 n개 이상 m개 이하. {0,1} 은 ?와 같은 의미다.
  • {n,}  : 앞 문자가 n개 이상. 위의 형태에서 m이 생략된 형태이다. {0,} 이면 *와 같고 {1,} 이면 +와 같은 의미이다.
  • ()  : 하나의 패턴구분자 안에 서브 패턴을 지정해서 사용할 경우 괄호로 묶어주는 방식을 사용한다.

3.3 패턴변경자

패턴구분자가 끝나면 그 뒤에 쓰는 것으로, 패턴에 일괄적으로 변경을 가할 때 사용한다.

  • i : 패턴을 대소문자 구분 없이 검사한다. 이 변경자를 사용할 경우 [a-z]로만 검사해도 자동으로 [a-zA-Z]와 같은 기능을 하게 된다. 가장 많이 쓰이는 패턴.
  • s : 임의의 한 문자를 가리키는 . 메타 문자에 개행 문자(\\n)도 포함시키도록 한다. 이 변경자를 사용하면 .이 줄바꿈도 임의의 한 문자로 취급하여 찾는다.
  • m : 주어진 문자열에 줄바꿈이 있을 경우, 여러 줄로 취급하여 검사한다. (줄바꿈이 없다면 써도 의미가 없다.) 원래 정규표현식을 쓸 때 줄바꿈은 무시되는데, 이걸 사용하면 줄바꿈을 적용해서 검사한다. 그리고 ^은 한 줄의 시작, $는 한 줄의 끝으로 의미가 달라진다.
  • x : 공백 문자를 찾지 않고 무시해 버린다. 단, 이스케이프(역슬래쉬하고 같이 쓸 경우)하거나 문자 클래스 안에 있을 경우에는 이걸 써도 공백을 찾는다.

3.4 예제

기본적인 정규식

  • ^[0-9]*$  : 숫자
  • ^[a-zA-Z]*$  : 영문자. 패턴변경자를 써서 /^[a-z]*$/i 같이 쓸 수 있다.
  • ^[가-힣]*$  : 한글(유니코드를 지원하는 정규식 엔진에 한정)
  • ^[a-zA-Z0-9]*$  : 영어/숫자

3.5 탐욕적 및 게으른 수량자

정규표현식에서는 일치하는 패턴을 찾는 횟수제한이 없어 필요이상의 상황을 연출하기도 하는데 이것은 의도적으로 수량자를 탐욕적으로 만들었기 때문이다. 문법에서 말하는 탐욕적 수량자란 가능하면 가장 큰 덩어리를 찾는다는 뜻이며, 반대의 개념인 게으른 수량자(Lazy Quantifier)는 패턴에 근접하는 최소한의 덩어리를 찾는다.

  • 탐욕적 수량자 : *, +, {n,}
  • 게으른 수량자 : *?, +?, {n,}?

사용 예(Python) :

>>> data = "<div> <p>First</p> <p>Second</p></div>"
>>> import re
>>> re.findall(r"<p>(.+)</p>", data)  # 기본적으로 탐욕적인 매칭
['First</p> <p>Second']
>>> re.findall(r"<p>(.+?)</p>", data)  # 게으른 수량자 사용
['First', 'Second']

사실 제대로 구현한 HTML파서는 정규식에 유한상태기계를 결합한 '오토마타'를 사용하지 게으른 수량자를 사용하지 않는다. 물론 jQuery를 비롯해 언어별로 HTML파서는 거의 만들어져 있으므로 HTML파서를 또 구현할 필요는 전혀 없음.

4 공부하는 방법

정규표현식을 당장 배우고 싶다면 이 사이트를 이용하면 step-by-step 방식으로 매우 빨리 배울 수 있다. 프로그래밍 언어에서 배우고 싶으면 자바스크립트의 정규식 엔진을[4], 쉽게 배우고 싶으면 Perl이나 Ruby를 추천한다. 단 Perl은 언어의 문법 자체가 괴랄하므로 쉽게 쓸 수 있는 Ruby가 더 권장된다. 원조는 Perl이라지만 펄 언어 자체를 공부하는 부담이 Ruby보다 크기 때문이다.

저것도 영 아니라면, 이 사이트에서 연습을 하자.
이 사이트에서 정규식 자체를 시각화해준다. 문서에 그림파일로 넣기에도 좋다.

Webhacking.kr에서 정규표현식을 다루는 문제가 하나 있다.
  1. Perl Compatible Regular Expressions. 이름에서 보이듯이 Perl 호환 정규표현식이지만 완전한 호환은 아니다.
  2. 물론, 오늘날에는 전부는 아니더라도 다른 정규표현식에 대응되는 명령들도 기본으로 포함하고 있는 유닉스나 리눅스 배포판들이 많다. 예를들어, grep 대신 Extended 정규표현식을 사용하는 egrep, sed 대신 PCRE 정규표현식을 사용하는 psed 등이 그것이다.
  3. 원래 정규표현식은 노엄 촘스키가 만든 촘스키 위계 3유형에 속하는 정규 문법에 대응되는데, PCRE에는 정규 문법에서 허용되지 않는 역참조(이전에 매칭된 부분문자열과 같은 패턴을 다시 매칭)와 같은 기능을 추가로 제공한다.
  4. 브라우저에서 F12만 누르면 즉시 사용가능