정규 표현식에는 검사 결과를 재활용할 수 있는 역 참조(Back Reference)라는 것이 있다. 검사 패턴으로 매칭된 결과를 다시 참조한다고 해서 역 참조라고 하는 것 같다. 이번 포스팅에서는 역참조에 대해서 알아보려고 한다.
이번 포스팅에서는 re.sub를 주로 사용하므로 사용법을 모르는 분들은 아래 포스팅을 미리 보고 오면 도움이 된다.
[정규 표현식] re 모듈 알아보기 (2) 대체하기 (feat. re.sub)
역 참조(Back reference)에 대해서 알아보자.
1) 역 참조란?
역 참조는 특정 패턴 그룹이 매칭된 경우 매칭 결과를 참조하는 것을 의미한다. 역 참조는 패턴 그룹 즉, 괄호 '( )'으로 묶인 패턴을 매칭하는 경우 백 슬래시와 숫자 '\숫자' 형식으로 표현한다. 아래 그림은 문자열 1234hgi에 대하여 패턴 (\d+)\1을 검사하고 매칭 결과를 역 참조하는 과정을 나타낸 것이다.
패턴 (\d+)은 문자열 1234를 매칭하고 \1로 표시된 곳에서 역 참조를 통하여 매칭 결과 1234를 저장하게 된다.
그러나...
역 참조는 정말 좋은 기능이지만 패턴 매칭 작업에 역 참조라는 작업이 추가됨에따라 계산량이 많아질 수 있다. 따라서 용량이 큰 텍스트 파일에는 역 참조가 실행을 더 느리게 만들 수 있다.
2) 활용
이제 원리를 알았으니 코드를 통해서 테스트 해보자. 역 참조는 특정 패턴을 역 참조로 저장된 것으로 대체하거나 패턴으로 재활용할 수 있다.
a. 대체하기
아래 코드는 패턴 그룹 2개를 각각 역 참조 하여 문자열을 바꾸는 것이다.
import re
string = '010-1111'
re.sub(r'(\d+)-(\d+)', r'\2__\1', string) ## 역 참조 두 두번 일어난다.
위 코드의 동작 원리는 다음과 같다. 먼저 패턴 '(\d+)-(\d+)'이 010-1111을 매칭한다. 다음으로 첫 번째 패턴 그룹 '(\d+)'(파란 박스)이 010을 매칭하고 역 참조를 통해 \1에 저장된다. 두 번째 패턴 그룹 '(\d+)'(초록 박스)이 1111을 매칭하고 역 참조를 통해 \2에 저장된다. 마지막으로 매칭된 010-1111은 \2__\1에 의하여 1111__010으로 바뀌게 된다.
위 코드를 실행시키면 예상대로 문자열이 바뀌는 것을 알 수 있다.
b. 패턴으로 재활용
역 참조는 패턴으로 재활용할 때 사용될 수 있다. 특히 특정 패턴을 중심으로 좌우에 같은 문자가 있는 패턴을 추출할 때 많이 사용된다. 아래 코드는 알파벳과 공백으로 이루어진 문자열을 중심으로 좌우에 각 괄호 내부의 h와 숫자 조합을 갖는 패턴을 찾되 좌우가 똑같은 것을 매칭한 것이다. 좌우가 다른 <h3>My Blog<h5>은 매칭되지 않는다.
string = '<h1>Hello<h1>, <h2>World<h2>, <h3>My Blog<h5>'
pattern = re.compile(r'<(h[0-9])>[a-zA-Z\s]*<\1>')
print([x.group() for x in list(re.finditer(pattern, string))])
위 코드의 동작 원리는 다음과 같다. 다른 부분은 비슷하므로 여기서는 <h1>Hello<h1>를 매칭하는 과정을 보자. 먼저 패턴 <(h[0-9])>에 대하여 <h1>이 매칭된다 하지만 패턴 그룹 매칭 (h[0-9])에 의하여 h1이 저장되어 있다. 다음으로 뒤에 <\1>에 의하여 저장되어 있던 h1을 각 괄호 안으로 갖고 오게 된다. 이에 따라 명목 (코드 상) 패턴은 <(h[0-9])>[a-zA-Z\s]*<\1>이지만 실질 패턴은 <h1>a-zA-Z\s]*<h1>이 된다. 첫 번째 h1은 패턴 그룹 매칭에 의한 것이고 끝 부분 h1은 역 참조에 의한 것임을 잊지 말자.
따라서 최종적으로 <h1>Hello<h1>이 매칭된다. 물론 <h2>World<h2>가 매칭되는 것이다.
코드를 실행하면 예상했던 대로 매칭이 된 것을 알 수 있다.
'프로그래밍 > 정규표현식' 카테고리의 다른 글
[정규 표현식] 응용 (1) URI(URL)에서 정보 가져오기 with Python (2) | 2022.10.11 |
---|---|
[정규 표현식] re 모듈 알아보기 (3) finditer 사용법과 findall과의 차이 (0) | 2022.10.01 |
[정규 표현식] re 모듈 알아보기 (2) 대체하기 (feat. re.sub) (0) | 2022.09.30 |
[정규 표현식] 메타 문자 알아보기 (6) | 2022.09.26 |
[정규 표현식] 알파벳 패턴 대소문자 상관없이 매칭하기 (feat. (?i), re.IGNORECASE) (0) | 2022.09.18 |
댓글