Python 정규 표현식 시작하기
목차
정규 표현식의 기초, 메타 문자
📌 정규 표현식에서 사용하는 메타 문자(meta characters)에는 다음과 같은 것들이 있다.
💡 메타 문자란 원래 그 문자가 가진 뜻이 아닌 특별한 용도로 사용되는 문자를 말한다.
. ^ $ * + ? { } [ ] \ | ( )
정규 표현식에 위 메타 문자들이 사용되면 특별한 의미를 갖게 된다.
문자 클래스 [ ]
📌 문자 클래스로 만들어진 정규식은 "[와 ] 사이의 문자들과 매치" 라는 의미를 갖는다.
💡 문자 클래스를 만드는 메타 문자인 [ ] 사이에는 어떤 문자도 들어갈 수 있다.
정규 표현식이 [abc] 라면 이 표현식의 의미는 "a, b, c 중 한 개의 문자와 매치"를 뜻한다. 문자열 "a", "before", "dude" 가 정규식 [abc]와 어떻게 매치되는지 알아본다.
- "a"는 정규식과 일치하는 문자인 "a"가 있으므로 매치
- "before"는 정규식과 일치하는 문자인 "b"가 있으므로 매치
- "dude"는 정규식과 일치하는 문자인 a, b, c 중 어느 하나도 포함하고 있지 않으모로 매치되지 않음
[ ] 안의 두 문자 사이에 하이픈(-)을 사용하게 되면 두 문자 사이의(From - To)를 의미한다. 예를 들어 [a-c]라는 정규 표현식은 [abc]와 동일하고 [0-5]는 [012345]와 동일하다.
다음은 하이픈(-)을 이용한 문자 클래스의 사용 예이다.
- [a-zA-Z] : 알파벳 모두
- [0-9] : 숫자
문자 클래스([ ]) 내에는 어떤 문자나 메타 문자도 사용할수 있지만 주의해야 할 메타 문자가 1가지 있다. 그것은 바로 ^
인데, 문자 클래스 내에 ^
메나 문자가 사용될 경우에는 반대(not)라는 의미를 갖는다. 예를 들어 [^0-9]
라는 정규 표현식은 숫자가 아닌 문자만 매치된다.
[자주 사용하는 문자 클래스]
[0-9] 또는 [a-zA-Z] 등은 무척 자주 사용하는 정규표현식이다. 자주 사용하는 정규식들은 별도의 표기법으로 표현할 수 있다.
- \d : 숫자와 매치, [0-9]와 동일한 표현식이다.
- \D : 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식이다.
- \s : whitespace 문자와 매치, [\t\n\r\f\v]와 동일한 표현식이다. 맨 앞의 빈칸은 공백문자(space)를 의미한다.
- \S : whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식이다.
- \w : 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-0]와 동일한 표현식이다.
- \W : 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-9]와 동일한 표현식이다.
대문자로 사용된 것은 소문자의 반대일을 추측할 수 있다.
Dot(.)
📌 정규 표현식의 Dot(.) 메타 문자는 줄바꾼 문자인 \n
를 제외한 모든 문자와 매치됨의 의미한다.
💡 정규식 작성 시 옵션으로 re.DOTALL 이라는 옵션을 주면 \n 문자와도 매치가 된다.
a.b
위 정규식의 의미는 다음과 같다.
"a + Dot(.)문자 + b"
따라서 정규식 a[.]b
는 "a.b"라는 문자열과는 매치되고 "a0b" 라는 문자와는 매치되지 않는다.
💡 . 는 \n 을 제외한 모든 문자와 매치되는데 심지어 \n 문자와도 매치되게 할 수도 있다. 정규식 작성시 옵션으로 re.DOTALL 이라는 옵션을 주면 \n 문자와도 매치되게 할 수 있다.
반복 (*)
ca*t
*
바로 앞에 있는 문자 a
가 0부터 무한대로 반복될 수 있다는 의미이다.
💡 여기서 * 메타문자의 반복갯수가 무한개까지라고 표현했는데 사실 메모리 제한으로 2억개 정도만 가능하다고 한다.
다음과 같은 문자열들이 모두 매치된다.
정규식 | 문자열 | Match 여부 | 설명 |
ca*t | ct | Yes | "a"가 0번 반복되어 매치 |
ca*t | cat | Yes | "a"가 0번 이상 반복되어 매치 (1번 반복) |
ca*t | caaat | Yes | "a"가 0번 이상 반복되어 매치 (3번 반복) |
반복 (+)
📌 반복을 나타내는 또 다른 메타 문자로 +
가 있다. +
는 최소 1번 이상 반복될 때 사용한다. 즉, *
가 반복 횟수 0부터라면 +
는 반복 횟수 1부터인 것이다.
ca+t
위 정규식의 의미는 다음과 같다.
"c + a(1번 이상 반복) + t"
위 정규식에 대한 매치여부는 다음 표와 같다.
정규식 | 문자열 | Match 여부 | 설명 |
cat+t | ct | No | "a"가 -번 반복되어 매치되지 않음 |
cat+t | cat | Yes | "a"가 1번 이상 반복되어 매치 (1번 반복) |
cat+t | caaat | Yes | "a"가 1번 이상 반복되어 매치 (3번 반복) |
반복 ({m, n}, ?)
📌 반복 횟수를 3회만 또는 1회부터 3회까지만으로 제한하고 싶을 경우. { } 메타 문자를 이용하면 반복 횟수를 고정시킬 수 있다.{m, n} 정규식을 사용하면 반복 횟수가 m부터 n까지인 것을 매치할 수 있다. 또한 m 또는 n을 생략할 수도 있다. 만약 {3,} 처럼 사용하면 반복 횟수가 3이상인 경우이고 {,3} 처럼 사용하면 반복 횟수가 3이하인 것을 의미한다. 생략된 m은 0과 동일하며, 생략된 n은 무한대 (2억개 미만)의 의미를 갖는다.
💡 {1,} 은 + 와 동일하며 {0,} 은 * 와 동일하다.
1. {m}
ca{2}t
위 정규식의 의미는 다음과 같다.
"c + a(반드시 2번 반복) + t"
위 정규식에 대한 매치여부는 다음 표와 같다.
정규식 | 문자열 | Match 여부 | 설명 |
ca{2}t | cat | No | "a" 가 1번만 반복되어 매치되지 않음 |
ca{2}t | caat | Yes | "a" 가 2번 반복되어 매치 |
2. {m, n}
ca{2, 5}t
위 정규식의 의미는 다음과 같다.
"c + a(2~4회 반복) + t"
위 정규식에 대한 매치여부는 다음 표와 같다.
정규식 | 문자열 | Match 여부 | 설명 |
ca{2,5}t | cat | No | "a" 가 1번만 반복되어 매치되지 않음 |
ca{2,5}t | caat | Yes | "a" 가 2번 반복되어 매치 |
ca{2,5}t | caaaaat | Yes | "a"가 5번 반복되어 매치 |
3. ?
📌 반복은 아니지만 이와 비슷한 개념으로 ?
이 있다. ?
메타문자가 의미하는 것은 {0, 1}
이다.
ab?c
위 정규식의 의미는 다음과 같다.
"a + b(있어도 되고 없어도 된다) + c"
위 정규식에 대한 매치여부는 다음 표와 같다.
정규식 | 문자열 | Match 여부 | 설명 |
ab?c | abc | Yes | "b" 가 1번 사용되어 매치 |
ab?c | ac | Yes | "b" 가 0번 사용되어 매치 |
즉, b문자가 있거나 없거나 둘 다 매치되는 경우이다.
*
, +
, ?
메타문자는 모두 {m, n}
형태로 고쳐쓰는 것이 가능하지만 가급적 이해하기 쉽고 표현도 간결한 *
, +
, ?
메타 문자를 사용하는 것이 좋다.
파이썬에서 정규 표현식을 지원하는 re 모듈
📌 파이썬은 정규 표현식을 지원하기 위해 re(regular expression의 약어) 모듈을 제공한다. re 모듈은 파이썬이 설치될 때 자동으로 설치되는 기본 라이브러리로, 사용 방법은 다음과 같다.
>>> import re
>>> p = re.compile('ab*')
re.compile 을 이용하여 정규표현식(위 예에서는 ab*
) 을 컴파일하고 컴파일된 패턴객체(re.compile의 결과로 리턴되는 객체 p) 를 이용하여 그 이후의 작업을 수행할 것이다.
- 정규식 컴파일 시 특정 옵셩을 주는 것도 가능하다.
- 패턴이한 정규식을 컴파일한 결과이다.
정규식을 이용한 문자열 검색
📌 이제 컴파일 된 패턴 객체를 이용하여 검색을 수행 해 보자. 컴파일 된 패턴 객체는 다음과 같은 4가지 메쏘드를 제공한다.
Method | 목적 |
match() | 문자열의 처음부터 정규식과 매치되는지 조사한다. |
search() | 문자열 전체를 검색하여 정규식과 매치되는지 조사한다. |
findall() | 정규식과 매치되는 모든 문자열(substring)을 리스트로 리턴한다. |
finditer() | 정규식과 매치되는 모든 문자열(substring)을 iterator 객체로 리턴한다. |
match, search는 정규식과 매치될 때에는 match 객체를 리턴하고 매치되지 않을 경우에는 None을 리턴한다.
💡 match 객체란 정규식의 검색 결과로 리턴되는 객체이다.
>>> import re
>>> p = re.compile('[a-z]+')
match
📌 match 메서드는 문자열의 처음부터 정규식과 매치되는지 조사한다. 위 패턴에 match 메서드를 수행해 보다.
"python" 이라는 문자열은 [a-z]+
정규식에 부합되므로 match 객체가 리턴된다.
"3 python"이라는 문자열은 처음에 나오는 3이라는 문자가 정규식 [a-z]+
에 부합되지 않으므로 None이 리턴된다.
match의 결과로 match 객체 또는 None이 리턴되기 때문에 파이썬 정규식 프로그램은 보통 다음과 같은 흐름으로 작성한다.
search
📌 컴파일된 패턴 객체 p를 가지고 이번에는 search 메서드를 수행해 본다.
"3 python"이라는 문자열의 첫 번째 문자는 "3"이지만 search 는 문자열의 처음부터 검색하는 것이 아니라 문자열 전체를 검색하기 때문에 "3" 이후의 "python"이라는 문자열과 매치된다.
이렇듯 match 메서드와 search 메서드는 문자열의 처음부터 검색할지의 여부에 따라 다르게 사용해야 한다.
findall
📌 이번에는 findall 메서드를 수행해 보자.
"life is too short" 라는 문자열의 "life", "is", "too", "short"라는 단어들이 각각 [a-z]+
정규식과 매치되어 리스트로 리턴된다.
finditer
📌 이번에는 finditer 메서드를 수행해 보자.
finditer는 findall과 동일하지만 그 결과로 반복 가능한 객체(iterator object)를 리턴한다. 반복 가능한 객체가 포함하는 각각의 요소는 match객체이다.
match 객체의 메서드
📌 match 메서드와 search 메서드를 수행한 결과로 리턴 되었단 matc 객체에 대해서 알아본다.
- 어떤 문자열이 매치되었는가?
- 매치된 문자열의 인덱스는 어디서부터 어디까지인가?
method | 목적 |
group() | 매치된 문자열을 리턴한다. |
start() | 매치된 문자열의 시작 위치를 리턴한다. |
end() | 매치된 문자열의 끝 위치를 리턴한다. |
span() | 매치된 문자열의 (시작, 끝) 에 해당하는 튜플을 리턴한다. |
match 메서드를 수행한 결과로 리턴된 match 객체의 start()의 결과값은 항상 0일 수밖에 없다. match메서드는 항상 문자열의 시작부터 조사하기 때문이다.
search 메서드를 사용했다면 start()의 값은 다음과 같이 다르게 나올 것이다.
🔔 예) 모듈 단위로 수행하기
re.compile을 이용하여 컴파일된 패턴 객체로 그 이후 작업을 수행했다. re모듈은 이 것을 좀 축약한 형태의 밥법을 제공한다.
위 코드가 축약된 형태는 다음과 같다.
위 예처럼 사용하면 컴파일과 match 메서드를 한번에 수행할 수 있다. 보통 한 번 만든 패턴 객체를 여러번 사용해야 할 때는 이 방법보다 re.compile을 사용하는 것이 유리하다.
컴파일 옵션
📌 정규식을 컴파일할 떄 다음과 같은 옵션을 사용할 수 있다.
- DOTALL(S) :
.
이 줄바꿈 문자를 포함하여 모든 문자와 매치할 수 있도록 한다. - IGNORECASE(I) : 대소문자에 관계없이 매치할 수 있도록 한다.
- MULTILINE(M) : 여러줄과 매치할 수 있도록 한다. ( ^ , $ 메타문자의 사용과 관계가 있는 옵션이다.)
- VERBOSE(X) : verbose 모드를 사용할 수 있도록 한다. (정규식을 보기 편하게 만들수 있고 주석등을 사용할 수 있게 된다.)
옵션을 사용할 때는 re.DOTALL
처럼 옵션명을 써도 되고 re.S
처럼 약어를 써도 된다.
DOTALL, S
메타 문자는 줄바꿈 문자(\n
)를 제외한 모든 문자와 매치되는 규칙이 있다. 만약 \n
문자도 포함하여 매치하고 싶다면 re.DOTAL
또는 re.S
옵션을 사용해 정규식을 컴파일하면 된다.
정규식이 a.b
인 경우 문자열 a\nb
는 매치되지 않음을 알 수 있다. \n
은 .
메타 문자와 매치되지 않기 때문이다. \n
문자와도 매치되게 하려면 다음과 같이 re.DOTALL 옵션을 사용해야 한다.
보통 re.DOTALL 은 여러줄로 이루어진 문자열에서 \n
에 상관없이 검색하고자 할 경우에 많이 사용한다.
IGNORECASE, I
re.IGNORECASE
또는 re.I
는 대소문자 구분없이 매치를 수행하고자 할 경우에 사용하는 옵션이다.
[a-z]
정규식은 소문자만을 의미하지만 re.I
옵션에 의해서 대/소문자 구분 없이 매치된다.
MULTILINE, M
📌 re.MULTILINE
또는 re.M
옵션은 조금 후에 설명할 메타 문자인 ^
, $
와 연관된 옵션이다. 이 메타 문자에 대해 간단히 설명하자면 ^
는 문자열의 처음을 의미하고, $
은 문자열의 마지막을 의미한다. 예를 들어 정규식이 ^python
인 경우 문자열의 처음은 항상 python으로 시작해야 매치되고, 만약 정규식이 python$
이라면 문자열의 마지막은 항상 python으로 끝나야 매치가 된다는 의미이다.
정규식 ^python\s\w+
은 "python"이라는 문자열로 시작하고 그 후에 whitespace, 그 후에 단어가 와야한다는 의미이다. 검색할 문자열 data는 여러줄로 이루어져 있다.
위 결과는 ^
메타 문자에 의해 python이라는 문자열이 사용된 첫 번째 라인만 매치가 된 것이다.
^
메타 문자를 문자열 전체의 처음이 아니라 각 라인의 처음으로 인식시키고 싶은 경우에 사용한다.
re.MULITINE 옵션으로 인해 ^
메타 문자가 문자열 전체가 아닌 각 라인의 처음이라는 의미를 갖게 되었다.
VERBOSE, X
📌 이해하기 어려운 정규식을 주석 또는 라인 단위로 구분할때 re.VERBOSE
또는 re.X
옵션을 이용하면 된다.
첫 번째와 두 번째 예를 비교해 보면 컴파일된 패턴 객체인 charref 는 모두 동일한 역할을 한다. 정규식이 복잡할 경우 두번째처럼 주석을 적고 여러 줄로 표현하는 것이 훨씬 가독성이 좋다.
reVERBOSE
옵션을 사용하면 문자열에 사용된 whitespace는 컴파일 시 제거된다(단 [ ] 내에 사용된ㄷ whitespace는 제외).
백슬래시 문제
📌 정규표현식을 파이썬에서 사용하려 할 때 혼란을 주게 되는 요소가 한가지 있는데 그것은 바로 백슬래시(\) 이다.
>>> p = re.compile(r'\\section')
위와 같이 정규식 문자열 앞에 r문자를 선행하면 이 정규식은 Raw String 규칙에 의하여 백슬래시 2개 대신 1개만 써도 두개를 쓴것과 동일한 의미를 갖게된다.
💡 만약 백슬래시를 사용하지 않는 정규식이라면 r의 유무에 상관없이 동일한 정규식이 될 것이다.