Jump to Python - 정규표현식과 XML
by Yoonkh
정규표현식과 XML
정규 표현식 살펴보기
정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용된다.
정규 표현식 시작하기
정규 표현식의 기초, 메타 문자
정규 표현식에서 사용하는 메타 문자(meta characters)에는 다음과 같은 것들이 있다.
. ^ $ * + ? { } [ ] \ | ( )
문자 클래스 [ ]
우리가 가장 먼저 살펴 볼 메타 문자는 바로 문자 클래스(character class)인 [ ]이다. 문자 클래스로 만들어진 정규식은 “[와 ] 사이의 문자들과 매치”라는 의미를 갖는다.
Dot(.)
정규 표현식의 Dot(.) 메타 문자는 줄바꿈 문자인 \n
를 제외한 모든 문자와 매치됨을 의미한다.
반복 (*)
ca*t
이 정규식에는 반복을 의미한는 * 메타문자가 사용되었다. 여기서 사용된 *의 의미는 *바로 앞에 있는 문자 a가 0부터 무한대로 반복될 수 있다는 의미이다.
반복 (+)
반복을 나타내는 또 다른 메타 문자로 +
가 있다. +
는 최소 1번 이상 반복될 때 사용한다. 즉, *
가 반복 횟수 0부터라면 +
는 반복 횟수 1부터인 것이다.
ca+t
- 위 정규식의 의미는 다음과 같다.
“c + a(1번 이상 반복) + t”
반복 ({m,n}, ?)
{ } 메타 문자를 이용하면 반복 횟수를 고정시킬 수 있다. {m, n} 정규식을 사용하면 반복 횟수가 m부터 n까지인 것을 매치할 수 있다. 또한 m 또는 n을 생략할 수도 있다. 만약 {3,} 처럼 사용하면 반복 횟수가 3 이상인 경우이고 {,3} 처럼 사용하면 반복 횟수가 3 이하인 것을 의미한다
※ {1,}
은 +
와 동일하며 {0,}
은 *
와 동일하다.
1. {m}
ca{2}t
“c + a(반드시 2번 반복) + t”
2. {m, n}
ca{2,5}t
“c + a(2~5회 반복) + t”
파이썬에서 정규 표현식을 지원하는 re 모듈
파이썬은 정규 표현식을 지원하기 위해 re(regular expression의 약어) 모듈을 제공한다
>>> import re
>>> p = re.compile('ab*')
re.compile 을 이용하여 정규표현식(위 예에서는 ab*
)을 컴파일하고 컴파일된 패턴객체(re.compile의 결과로 리턴되는 객체 p)를 이용하여 그 이후의 작업을 수행할 것이다.
정규식을 이용한 문자열 검색
Method | 목적 |
---|---|
match() | 문자열의 처음부터 정규식과 매치되는지 조사한다. |
search() | 문자열 전체를 검색하여 정규식과 매치되는지 조사한다. |
findall() | 정규식과 매치되는 모든 문자열(substring)을 리스트로 리턴한다. |
finditer() | 정규식과 매치되는 모든 문자열(substring)을 iterator 객체로 리턴한다 |
match 객체의 메서드
method | 목적 |
---|---|
group() | 매치된 문자열을 리턴한다 |
start() | 매치된 문자열의 시작 위치를 리턴한다 |
end() | 매치된 문자열의 끝 위치를 리턴한다 |
span() | 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 리턴한다. |
컴파일 옵션
정규식을 컴파일할 때 다음과 같은 옵션을 사용할 수 있다.
-
DOTALL(S) - . 이 줄바꿈 문자를 포함하여 모든 문자와 매치할 수 있도록 한다.
-
IGNORECASE(I) - 대소문자에 관계없이 매치할 수 있도록 한다.
-
MULTILINE(M) - 여러줄과 매치할 수 있도록 한다. (
^
,$
메타문자의 사용과 관계가 있는 옵션이다) -
VERBOSE(X) - verbose 모드를 사용할 수 있도록 한다. (정규식을 보기 편하게 만들수 있고 주석등을 사용할 수 있게된다.)
옵션을 사용할 때는 re.DOTALL처럼 전체 옵션명을 써도 되고 re.S처럼 약어를 써도 된다.
강력한 정규 표현식의 세계로
메타 문자
-
+
,*
,[]
,{}
등의 메타문자는 매치가 진행될 때 현재 매치되고 있는 문자열의 위치가 변경된다. (보통 소모된다고 표현한다.) 하지만 이와 달리 문자열을 소모시키지 않는 메타 문자들도 있다. -
|
|
메타문자는 “or”의 의미와 동일하다.A|B
라는 정규식이 있다면 이것은 A 또는 B라는 의미가 된다.
-
^
^
메타문자는 문자열의 맨 처음과 일치함을 의미한다. 이전에 알아보았던 컴파일 옵션re.MULTILINE
을 사용할 경우에는 여러줄의 문자열에서는 각 라인의 처음과 일치하게 된다.
-
$
$
메타문자는^
메타문자의 반대의 경우이다.$
는 문자열의 끝과 매치함을 의미한다.
-
\A
\A
는 문자열의 처음과 매치됨을 의미한다
-
\Z
\Z
는 문자열의 끝과 매치됨을 의미한다
-
\b
\b
는 단어 구분자(Word boundary)이다
-
\B
\B
메타문자는\b
메타문자의 반대의 경우이다.
그룹핑
ABC라는 문자열이 계속해서 반복되는지 조사하는 정규식을 작성하고 싶다고 하자. 어떻게 해야 할까? 지금까지 공부한 내용으로는 위 정규식을 작성할 수 없다. 이럴 때 필요한 것이 바로 그룹핑(Grouping) 이다.
- 위의 경우는 다음처럼 그룹핑을 이용하여 작성할 수 있다.
(ABC)+
그룹을 만들어 주는 메타문자는 바로 (
과 )
이다.
>>> p = re.compile('(ABC)+')
>>> m = p.search('ABCABCABC OK?')
>>> print(m)
<_sre.SRE_Match object at 0x01F7B320>
>>> print(m.group())
ABCABCABC
- 다음의 예를 보자!!!
>>> p = re.compile(r"\w+\s+\d+[-]\d+[-]\d+")
>>> m = p.search("park 010-1234-1234")
\w+\s+\d+[-]\d+[-]\d+
은이름 + " " + 전화번호
형태의 문자열을 찾는 정규표현식이다
그룹핑된 문자열 재참조하기
정규식 내에 그룹이 무척 많아진다고 가정해 보자. 예를 들어 정규식 내에 그룹이 10개 이상만 되어도 매우 혼란스러울 것이다. 거기에 더해 정규식이 수정되면서 그룹이 추가, 삭제되면 그 그룹을 인덱스로 참조했던 프로그램들도 모두 변경해 주어야 하는 위험도 갖게 된다.
-
약 그룹을 인덱스가 아닌 이름(Named Groups)으로 참조할 수 있다면 어떨까? 그렇다면 이런 문제들에서 해방되지 않을까?
- 이러한 이유로 정규식은 그룹을 만들 때 그룹명을 지정할 수 있게 했다.
(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)
전방탐색
정규식에 막 입문한 사람들이 가장 어려워하는 것이 바로 전방 탐색(Lookahead Assertions) 확장 구문이다
>>> p = re.compile(".+:")
>>> m = p.search("http://google.com")
>>> print(m.group())
http:
-
정규식
.+:
과 일치하는 문자열로 “http:”가 리턴되었다. 하지만 “http:” 라는 검색 결과에서 “:”을 제외하고 출력하려면 어떻게 해야 할까? 위 예는 그나마 간단하지만 훨씬 복잡한 정규식이어서 그룹핑은 추가로 할 수 없다는 조건까지 더해진다면 어떻게 해야 할까? -
이럴 때 사용할 수 있는 것이 바로 전방 탐색이다. 전방 탐색에는 긍정(Positive)과 부정(Negative)의 2종류가 있고 다음과 같이 표현된다.
-
긍정형 전방 탐색(
(?=...)
) -...
에 해당되는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소모되지 않는다. -
부정형 전방 탐색(
(?!...)
) -...
에 해당되는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소모되지 않는다.
-
긍정형 전방 탐색
긍정형 전방 탐색을 이용하면 http:의 결과를 http로 바꿀 수 있다
>>> p = re.compile(".+(?=:)")
>>> m = p.search("http://google.com")
>>> print(m.group())
http
- 정규식 중
:
에 해당하는 부분이 긍정형 전방탐색 기법이 적용되어(?=:)
으로 변경되었다. 이렇게 되면 기존 정규식과 검색에서는 동일한 효과를 발휘하지만:
에 해당되는 문자열이 정규식 엔진에 의해 소모되지 않아(검색에는 포함되지만 검색 결과에는 제외됨) 검색 결과에서는:
이 제거된 후 리턴되는 효과가 있다.
부정형 전방 탐색
.*[.](?!bat$).*$
- 확장자가 bat가 아닌 경우에만 통과된다는 의미이다. bat라는 문자열이 있는지 조사하는 과정에서 문자열이 소모되지 않으므로 bat가 아니라고 판단되면 그 이후 정규식 매칭이 진행된다.
문자열 바꾸기
sub 메서드를 이용하면 정규식과 매치되는 부분을 다른 문자로 쉽게 바꿀 수 있다.
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
- sub 메서드의 첫 번째 입력 인수는 “바꿀 문자열(replacement)”이 되고, 두 번째 입력 인수는 “대상 문자열”이 된다. 위 예에서 볼 수 있듯이 blue 또는 white 또는 red라는 문자열이 colour라는 문자열로 바뀌는 것을 확인할 수 있다.
sub 메서드 사용 시 참조 구문 사용하기
sub 메서드를 사용할 때 참조 구문을 사용할 수 있다
>>> p = re.compile(r"(?P<name>\w+)\s+(?P<phone>(\d+)[-]\d+[-]\d+)")
>>> print(p.sub("\g<phone> \g<name>", "park 010-1234-1234"))
010-1234-1234 park
- 위 예는
이름 + 전화번호
의 문자열을전화번호 + 이름
으로 바꾸는 예이다.sub
의 바꿀 문자열 부분에\g<그룹명>
을 이용하면 정규식의 그룹명을 참조할 수 있게된다.
sub 메서드의 입력 인수로 함수 넣기
sub 메서드의 첫 번째 입력 인수로 함수를 넣을 수도 있다.
>>> def hexrepl(match):
... "Return the hex string for a decimal number"
... value = int(match.group())
... return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'
Subscribe via RSS