5 min to read
문자열 처리의 해결사. 정규표현식을 알아보자 ①
텍스트 검색의 동반자 정규표현식의 기본 문법부터 차근차근 알아봅시다.
이 포스트는 "정규식" 시리즈의 1번째 포스트 입니다.
-
문자열 처리의 해결사. 정규표현식을 알아보자 ①
텍스트 검색의 동반자 정규표현식의 기본 문법부터 차근차근 알아봅시다.
-
문자열 처리의 해결사. 정규표현식을 알아보자 ②
정규표현식을 더욱 더 간결하게 사용할 수 있도록 도와주는 신택스 슈가에 대해서 알아봅시다.
-
문자열 처리의 해결사. 정규표현식을 알아보자 ③
정규표현식에서 문자열 조작을 다뤄주는 캡처에 대해서 알아봅시다.
-
문자열 처리의 해결사. 정규표현식을 알아보자 ④
게으른 수량자인 겸허 수량자를 이용해, 캡쳐를 다루는 방법과 문자열 치환에 대해서 알아봅시다.
들어가며
컴퓨터를 사용하면서, 프로그래밍이 아니더라도, 텍스트 검색을 할 경우는 참 많습니다. 웹 페이지에서 ctrl+f
로 필요한 내용이 있는지 찾아보기도 하고, 한글 문서를 찾아야 하는데, 제목은 기억이 안나서 *.hwp
로 검색을 하기도 하죠.
우리가 기본적으로 사용하는 텍스트검색의 와일드카드 문자는
- 임의의 하나 이상의 문자를 나타내는
*
- 임의의 한 문자를 나타내는
?
- 그리고 이 문자들을 문자 그자체로 검색할 수 있게 해주는 일종의 이스케이프 문자인
~
가 끝입니다. 일반적인 용도로는 꽤나 요긴하게 쓰이긴 하지만, 기능이 부실한것은 사실입니다.
이번 포스트에서는 이러한 간단한 와일드카드 문자에서 한걸음 더 나아간 정규표현식의 간단한 기능부터 차근차근 알아보도록 하겠습니다.
정규표현식의 기본 중 기본 : 접합, 선택, 반복
수학에서 셈을 배울 때, 기본적으로 배우는 사칙연산이 있듯이, 정규 표현식에도 그에 상응하는 기본이 되는 연산이 있습니다. 제목에도 나와잇듯이 접합, 선택 그리고 반복 연산입니다. 정규표현식을 완벽하게 이해하는것은 쉬운 일이 아니지만, 이 세가지 기본연산은 간단하게 익힐 수 있으니, 주저하지 말고, 절 믿고 따라오시길 바랍니다.
정규 표현식 리터럴
프로그래밍 언어를 약간이라도 다루어 봤다면, 리터럴이라는 개념을 들어본 적 있을겁니다. 잘 모르겠다면, 상수라고 생각하면 됩니다. 변하는 변수의 반대 개념이지요.
정규 표현식에서 특정한 문자 하나에 일치하는 정규 표현식은, 미리 정규표현식에 예약된 문자(. $ ^ | * 등등)을 제외하고는 찾고싶은 문자를 그대로 사용하면 됩니다. a에 일치하는 정규 표현식은 a
라고 생각하면 됩니다.
접합 연산
정규 표현식이란, 위에서 간단하게 언급했듯, 텍스트 검색을 할 때 쓰이는 도구입니다. 하단에 적힌 lorem ipsum 텍스트를 잠깐 주목해 주세요.
lorem.txt
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum."
이 텍스트에서 labor
이라는 텍스트를 찾기 위해서는 어떤 정규표현식을 사용해야 할까요? 일반적으로는 labor
이라는 글자 그대로를 입력할 것이고, 이는 올바른 정규 표현식이 맞습니다.
나중에 정규 표현식을 사용하는 도구에 관해서 자세히 설명하겠지만, 정규 표현식을 통해서 검색을 하는 잘 알려진 툴인 grep
을 이용해서 실험해 보겠습니다.
user@computer:~/some/directory$ grep labor lorem.txt
“Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. sunt in culpa qui officia deserunt mollit anim id est laborum.”
이런 결과를 볼 수 있습니다. labor
이라는 정규 표현식은 사실 찬찬히 뜯어보면, l
, a
, b
, o
, r
라는 글자들에 대응하는 정규 표현식 리터럴들이 접합된 형태입니다. 이 접합 연산이라는 것은 정규표현식의 공기와 같습니다. 공기는 보이지 않지만, 우리가 목숨을 유지하는데에 꼭 필요한것처럼, 접합연산 또한 그러합니다.
만약 접합연산을 정규 표현식을 서로 붙여서 쓰는 방식이 아닌 _
등의 다른 기호를 통해서 나타내었다면 아까 labor라는 문자열을 검색하는 데에도 l_a_b_o_r
이런식으로 사용했어야 했기 때문에, 상당히 불편했을 것입니다.
선택 연산
선택 연산은 말 그대로 여러 패턴중에 한가지를 선택해서 일치시키는 연산을 의미합니다. |
연산자를 사용하여, 선택후보 패턴들을 나열합니다. 논리 연산에 빗대어 설명한다면 OR 연산과 비슷하다고 할 수 있겠습니다. 매우 간단한 예로, abcd와 ABCD 두 문자열을 모두 찾고 싶을 떄는, 아래와 같은 정규 표현식을 사용하여 매칭시킬 수 있습니다.
abcd|ABCD
반복 연산
정규 표현식은 반복 또한 가능합니다. 프로그래밍 언어로 코드를 작성 할 때, 반복되는 부분을 일일히 복사-붙여넣기 하지 않고, for
문 등의 반복문을 쓰는것과 비슷한 이치라고 생각하면 됩니다. 정규 표현식에서의 반복 연산은 0 이상의(즉 0을 포함한) 임의의 횟수만큼을 반복함을 의미합니다.
정규 표현식에서 반복 연산을 표현하는 연산자는 *
입니다. 그러니까, 예를 들어서 r
이라는 정규 표현식이 있다면, (r)*
이라는 정규 표현식은 그 r
이라는 정규 표현식에 일치하는 문자열이 임의의(다시 말하지만 0번을 포함해서) 일치하는 정규 표현식을 의미합니다.
실용적인 예시로, 숫자만으로 구성된 길이 1 이상의 문자열을 찾는 정규표현식을 예로 들어보겠습니다.
(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*
가 됩니다. 여기서 괄호는 선택 연산의 우선순위가 접합 연산의 우선순위보다 떨어지기 때문에 선택 연산을 우선 한 다음 접합 연산을 해라고 명시적으로 말해두기 위해서 작성한 문법입니다. 혼합연산의 괄호와 기능이 동일하다고 생각하면 됩니다.
처음 정규표현식을 접할 때, *
가 일반 와일드카드 문자 검색과는 다르게, 임의의 문자 임의의 횟수번이 아니라서 잠깐 혼란이 올 수 있습니다만, r*
라는 정규 표현식을 \(r^*\) 로 생각한다면, 좀 혼란이 덜 하지 않을까 합니다.
여러 종류의 연산을 동시에 사용
정규 표현식의 세 연산을 동시에 사용할 수 없다면, 정규 표현식의 기능은 너무나도 빈약해지기 때문에, 여러 연산을 함께 사용합니다. 멀리 갈 필요도 없이, 방금 소개한 길이 1 이상의 숫자만으로 구성된 문자열을 찾는 정규 표현식이, 세 연산을 모두 사용한 올바른 정규 표현식의 예 입니다.
(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*
(0|1|2|3|4|5|6|7|8|9)
에서 선택 연산을 사용했고, (0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)
에서 접합 연산을, (0|1|2|3|4|5|6|7|8|9)*
에서 반복연산을 사용했습니다.
이 정규표현식을 소개하면서 간단히 지나가듯 말했지만, 정규 표현식의 세 연산에는 우선순위가 존재합니다.
결합 순위 | 정규표현식 | 수식 |
---|---|---|
높음 | 반복(*) | 제곱(^) |
↕ | 접합 | 곱셈(×) |
낮음 | 선택( | ) | 덧셈(+) |
이 우선순위를 고려해서 정규 표현식을 작성해야, 올바른 검색결과를 얻을 수 있음에 유의하시기 바랍니다.
마치며
이 세가지 연산은, 정규표현식에서 매우 중요한 연산이고, 많은 연산을 수행할 수 있지만, 이 세가지 연산만 사용해서 정규 표현식을 작성하면, 식이 너무 길다는 느낌을 지우기가 힘듭니다. 멀리 갈 필요도 없이,
(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*
가 얼마나 길게 느껴집니까? 다음 포스팅에서는, 이러한 장황한 정규 표현식을 간결히 나타내는 신택스 슈가와, 정규 표현식의 다양성을 높여줄 수 있는 특수한 용도로 사용되는 정규 표현식에서의 문자들을 알아보도록 하겠습니다.
끝까지 읽어주셔서 감사합니다. 혹시 읽다가, 궁금증이 생기셨거나, 틀렸다고 생각되는 부분이 있다면, 주저없이 댓글 달아주시면, 최대한 빠른 시일 내에 알아보고 답을 드리도록 하겠습니다.
참고한 자료
다양한 언어로 배우는 정규표현식 최신 엔진 구현과 이론적 배경을 배우다(신야 료마 , 스즈키 유스케 , 타카타 켄 지음)