Regular Expressions

정규식, 그 중에서도 파이썬의 re 패키지 사용법을 정리해둔다.

re

펄의 정규식 엔진과 유사한 기능이다.

정규식을 쓸 때는 r로 시작하는 raw string을 사용하는 것이 좋다. r이 붙은 raw string에서는 \가 이스케이프 문자로 해석되지 않는다. 그래서 r'\n'\n 두 글자로 해석되는 반면 '\n'은 개행문자 한 글자로 해석된다.

문법

특수문자

.

개행문자를 제외한 모든 문자 한 글자랑 매치한다.

^

문자열의 시작 위치와 매치한다.

$

문자열의 끝과 매치한다. 개행문자 바로 앞이다.

\

특수문자를 이스케이프하여 *?를 매치할 수 있게 해주거나, 특수 시퀀스를 시작한다.

파이썬의 문자열 리터럴에서도 \가 이스케이프 시퀀스로 사용되기 때문에, raw string 패턴을 쓰지 않으면 조심해야 한다. 파이썬의 파서가 인식하지 않으면 결과 문자열에 포함된다.

[]

글자의 집합을 나타낸다.

  • 한 글자씩 추가할 수 있다. [amk]a 또는 m 또는 k와 매치한다.
  • 특수문자 -와 두 개의 추가 캐릭터를 이용해서 문자의 범위를 나타낼 수도 있다. 예를 들어 [a-z]는 영문 소문자를, [0-5][0-9]00부터 59까지 매치한다. 만약 -[a\-z]와 같이 이스케이프 되거나, [-a]와 같이 집합의 가장 처음에 나타난다면 리터럴 -에 매치한다.
  • 특수문자는 집합 안에서 의미를 잃는다. 예를 들어 [(+*)](, +, *, ) 중 하나와 매치한다.
  • \w\S같은 문자 클래스가 집합 안에 올 수 있다.
  • 집합을 여집합으로 만듦(complementing the set)으로써 범위 안에 없는 글자를 매칭하게 할 수도 있다. 집합의 시작 문자가 ^이면, 이후에 오는 문자가 아닌 모든 글자와 매치된다. 예를 들어 [^5]5빼고는 다 매치하고, [^^]^빼고는 다 매치한다. ^는 집합 안에서 첫번째에 위치하지 않으면 특수한 역할을 하지 않는다.
  • ] 리터럴을 집합 안에서 매치하고 싶다면 백슬래시로 이스케이프 하거나 집합의 가장 처음에 위치하게 하면 된다. 예를 들어 [()[\]{}][]()[{}]는 동일한 의미로 괄호를 매치한다.

|

OR의 의미다. | 리터럴을 매치하고 싶다면 \|처럼 이스케이프하던가 아니면 [|] 처럼 집합으로 표현하면 된다.

(...)

괄호 안의 정규식과 매치하고 그 결과를 그룹으로 묶어서 시작과 끝 위치를 가진다. 매치가 완료되면 매칭된 그룹 내용을 알 수 있다.

괄호 자체를 매치하고 싶다면 \(\) 처럼 각각 이스케이프하거나 [(], [)]처럼 집합으로 표현하면 된다.

\number

number 번째 매칭된 그룹과 매치한다. 1-indexed를 사용한다. 예를 들어, (.+) \1the the55 55를 매치한다.

\b

빈 문자열(blank)과 매치한다. 문자의 시작이나 끝에서만 매치한다. r'\bfoo\b'foo, foo., (foo), bar foo baz와 매치하지만 foobarfoo3과는 매치하지 않는다.

\B

빈 문자열과 매치하지만, 문자의 시작이나 끝이 아닌 것만 매치한다. 따라서 r'py\B'python, py3, py2와 매치하지만 py, py., py!와는 매치하지 않는다. \b의 역연산이다.

\d

[0-9]

\D

[^0-9]

\s

유니코드 공백 문자 [ \t\n\r\f\v]와 매치한다.

\S

[^ \t\n\r\f\v]

\w

유니코드 문자열 패턴과 매치한다. [a-zA-Z0-9_]

\W

[^a-zA-Z0-9_]

모듈

re.compile(pattern, flags=0)

정규 표현식 패턴을 컴파일해서 정규 표현식 오브젝트로 만든다. 나중에 이 오브젝트로 match(), search() 등의 함수를 이용해서 매치할 수 있다.

result = re.match(pattern, string)

위의 코드는

prog = re.compile(pattern)
result = prog.match(string)

과 같다. 하지만, 같은 정규식 패턴이 프로그램에서 여러 번 나타나는 경우, re.compile()을 이용해서 정규 표현식 오브젝트를 만들어서 재사용하는 것이 더 효율적이다.

re.search(pattern, string, flags=0)

string을 스캔하면서 정규식 pattern과 매치하는 첫 번째 위치를 찾고 매치 오브젝트를 리턴한다. 더 이상 매치하는 패턴이 없으면 None을 리턴한다.

re.match(pattern, string, flags=0)

string의 처음에서 0개 이상의 문자가 정규식 pattern과 일치하면 매치 오브젝트를 리턴한다.

Match Object

매치 오브젝트는 조건식에서 항상 True로 평가된다.

MatchObject.group(args)

매치된 하나 이상의 서브 그룹을 리턴한다. 파라미터가 하나면 하나, 여러 개면 튜플로 리턴한다. 서브 그룹은 1-indexed이고 파라미터가 0이면 전체를 리턴한다.

m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
m.group(0) == 'Isaac Newton'  # entire match
m.group(1) == 'Isaac'
m.group(2) == 'Newton'
m.group(1, 2) == ('Isaac', 'Newton')

(?P<name>...) 정규식을 이용했다면 파라미터가 숫자가 아니라 그룹 이름이 될 수도 있다.

m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Steve Jobs")
m.group('first_name') == 'Steve'
m.group('last_name') == 'Jobs'

# can be indexed
m.group(1) == 'Steve'
m.group(2) == 'Jobs'
MatchObject.groups()

모든 매치된 서브 그룹을 튜플로 리턴한다.

How To

함수 이름 목적
match() 문자열의 시작 부분에서 정규식과 매치되는게 있는지 찾는다.
search() 정규식과 매치하는 위치를 찾으면서 문자열을 계속 훑어 나간다.
findall() 정규식과 매치하는 모든 부분 문자열을 찾아서 리스트로 리턴한다.
finditer() 정규식과 매치하는 모든 부분 문자열을 찾아서 이터레이터로 리턴한다.
  • match(), search()는 매치하는게 없으면 None을 리턴, 있으면 매치 오브젝트를 리턴

매치 오브젝트

함수 이름 목적
group() 정규식과 일치하는 문자열을 리턴
start() 매치 시작 위치
end() 매치 끝 위치
span() 매치의 (시작, 끝) 위치 튜플

가장 많이 사용하는 패턴

p = re.compile(r'regular expression')
m = p.match(' string to match ')
if m:
    print(m.group())
else:
    print('no')
p = re.compile(r'\d+')
p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
== ['12', '11', '10']