45.3 그룹 사용하기

지금까지 정규표현식 하나로만 매칭 여부를 판단했습니다. 이번에는 정규표현식을 그룹으로 묶는 방법을 알아보겠습니다. 정규표현식 그룹은 해당 그룹과 일치하는 문자열을 얻어올 때 사용합니다.

패턴 안에서 정규표현식을 ( )(괄호)로 묶으면 그룹이 됩니다. 다음은 공백으로 구분된 숫자를 두 그룹으로 나누어서 찾은 뒤 각 그룹에 해당하는 문자열(숫자)을 가져옵니다.

>>> m = re.match('([0-9]+) ([0-9]+)', '10 295')
>>> m.group(1)    # 첫 번째 그룹(그룹 1)에 매칭된 문자열을 반환
'10'
>>> m.group(2)    # 두 번째 그룹(그룹 2)에 매칭된 문자열을 반환
'295'
>>> m.group()     # 매칭된 문자열을 한꺼번에 반환
'10 295'
>>> m.group(0)    # 매칭된 문자열을 한꺼번에 반환
'10 295'

매치 객체의 group 메서드에 숫자를 지정하면 해당 그룹에 매칭된 문자열을 반환합니다. 숫자를 지정하지 않거나 0을 지정하면 매칭된 문자열을 한꺼번에 반환합니다. 그리고 groups 메서드는 각 그룹에 해당하는 문자열을 튜플로 반환합니다.

>>> m.groups()    # 각 그룹에 해당하는 문자열을 튜플 형태로 반환
('10', '295')

그룹 개수가 많아지면 숫자로 그룹을 구분하기가 힘들어집니다. 이때는 그룹에 이름을 지으면 편리합니다. 그룹의 이름은 ( )(괄호) 안에 ?P<이름> 형식으로 지정합니다.

  • (?P<이름>정규표현식)

다음은 함수를 호출하는 코드 print(1234)에서 함수의 이름 print와 인수 1234를 추출합니다.

>>> m = re.match('(?P<func>[a-zA-Z_][a-zA-Z0-9_]+)\((?P<arg>\w+)\)', 'print(1234)')
>>> m.group('func')    # 그룹 이름으로 매칭된 문자열 출력
'print'
>>> m.group('arg')     # 그룹 이름으로 매칭된 문자열 출력
'1234'

(?P<func>)(?P<arg>)처럼 각 그룹에 이름을 짓고 m.group('func'), m.group('arg')로 매칭된 문자열을 출력했습니다. 참고로 함수 이름은 문자로만 시작해야 하므로 첫글자는 [a-zA-Z_]로 판단해줍니다. 또한, print 뒤에 붙은 (, )는 정규표현식에 사용하는 특수 문자이므로 앞에 \를 붙여줍니다.

지금까지 그룹에 해당하는 문자열을 가져왔습니다. 그러면 그룹 지정 없이 패턴에 매칭되는 모든 문자열을 가져오려면 어떻게 해야 할까요? 이때는 findall 함수를 사용하며 매칭된 문자열을 리스트로 반환합니다.

  • re.findall(패턴, 문자열)

다음은 문자열에서 숫자만 가져옵니다.

>>> re.findall('[0-9]+', '1 2 Fizz 4 Buzz Fizz 7 8')
['1', '2', '4', '7', '8']
참고 | 그룹과 *, + 활용하기

+*을 조합하여 사용할 때는 그룹으로 묶어서 사용합니다. (.[a-z]+)*는 점과 영문 소문자가 1개 이상 있는지 판단하고, 이것 자체가 0개 이상인지 판단합니다. 즉, 규칙은 반드시 지켜야 하지만 있어도 되고 없어도 되는 상황에 사용합니다.

>>> re.match('[a-z]+(.[a-z]+)*$', 'hello.world')    # .world는 문자열이므로 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 11), match='hello.world'>
>>> re.match('[a-z]+(.[a-z]+)*$', 'hello.1234')     # .1234는 숫자이므로 패턴에 매칭되지 않음
>>> re.match('[a-z]+(.[a-z]+)*$', 'hello')          # .뒤에 문자열이 없어도 패턴에 매칭됨
<_sre.SRE_Match object; span=(0, 5), match='hello'>