본문 바로가기

개발

정규표현식의 표현식 그룹 이름 지정 (Named Capture Group)에 대해 알아봅시다.

반응형

정규표현식에서 ?P<name>을 원하는 패턴 앞에 써주면 특정 표현식에 대해 이름으르 그룹을 지정할 수 있습니다. 예를 들어, 아래와 같이 사용하면 major, minor, patch 라는 이름으로 표현식 그룹을 만들 수 있습니다.

(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)

괄호로 묶는 범위만큼 그룹이 생성되어 특정 그룹을 지정할 수 있습니다. (정규표현식에서 이렇게 일부 패턴을 뽑아내는 것을 캡처(Capture)라고 부릅니다.) 파이썬에서는 아래와 같이 사용할 수 있습니다.

import re
m = re.search(r"v(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)", "v1.2.3")

m.group('major')  # '1'
m.group('minor')  # '2'
m.group('patch')  # '3'

그룹 이름을 지정하지 않았다면, 아래와 같이 위치로 그룹을 지정해야 합니다.

import re
m = re.search(r"v(\d+)\.(\d+)\.(\d+)", "v1.2.3")

m.group(1)  # '1'
m.group(2)  # '2'
m.group(3)  # '3'

위치로 그룹을 지정하지 않고 이름으로 그룹을 지정하면 아래와 같은 이점을 얻을 수 있습니다.

  1. 각 그룹이 어떤 역할을 하는지를 명시함으로써 코드의 가독성을 높일 수 있습니다.
  2. 그룹의 순서(위치)에 영향을 받지 않고 원하는 부분을 추출할 수 있어 프로그래밍 언어와 조합되었을 때 활용도를 높일 수 있습니다.

2번 장점에 대해서 간단하게 예를 들어 보겠습니다. CLI에서 특정 패턴과 버전을 입력받아 major, minor, patch 버전을 추출하는 프로그램을 작성해 봅시다. CLI 프로그램을 직접 작성하진 않고, 해당 옵션(버전과 패턴)이 들어온다고 가정하고 코드를 작성하겠습니다.

import re

# 아래 version, pattern을 cli에서 받습니다.
version = "v1.2.3"
# 일반적으로 시멘틱 버저닝은 {major}.{minor}.{patch} 순서이지만 순서에 영향을 안받는 것을 보여주기 위해 아래와 같은 예제를 작성했습니다.
pattern = 'v{patch}.{minor}.{major}'

# {major}, {minor}, {patch}를 named capture regex로 변경합니다.
new_version = version.replace(".", "\.")  # Escape '.'
pattern = pattern.replace('{major}', '(?P<major>\d+)').replace('{minor}', '(?P<minor>\d+)').replace('{patch}', '(?P<patch>\d+)')

m = re.search(pattern, version)

m.group('major') # 3
m.group('minor') # 2
m.group('patch') # 1

위와 같이 구성하면, {major} , {minor}, {patch} 의 순서와 관계없이 버전을 올바르게 파싱 할 수 있습니다. (해당 코드를 응용하여 원하는 버전을 올리는 프로그램 등을 작성해 볼 수 있겠죠?)

Named Capture(이름으로 그룹 지정)은 프로그래밍 언어에 의존적이지 않는 정규표현식으로, 대부분의 프로그래밍 언어에서 사용할 수 있습니다. 예를 들어, 위의 파이썬 코드를 자바스크립트로도 작성할 수 있습니다. 단, 자바스크립트는 ?P<name> 에서 P를 제외하고 ?<name>으로 작성해야 합니다.

let version = "v1.2.3";
let pattern = 'v{patch}.{minor}.{major}';

// Escape '.'
let newVersion = version.replace(".", "\\.");

// {major}, {minor}, {patch} to named capture regex
pattern = pattern.replace('{major}', '(?<major>\\d+)').replace('{minor}', '(?<minor>\\d+)').replace('{patch}', '(?<patch>\\d+)');

let m = version.match(pattern);

console.log(m.groups.major); // 3
console.log(m.groups.minor); // 2
console.log(m.groups.patch); // 1

이상으로, 정규표현식의 Named Capture에 대해서 알아봤습니다.

반응형