세상의 변화에 대해 관심이 많은 이들의 Tech Blog search

파이썬 정규표현식(re) 사용법 - 07. 예제(숫자)

|

목차


파이썬 정규표현식(re) 사용법 - 01. Basic
파이썬 정규표현식(re) 사용법 - 02. 문자, 경계, flags
파이썬 정규표현식(re) 사용법 - 03. OR, 반복
파이썬 정규표현식(re) 사용법 - 04. 그룹, 캡처
파이썬 정규표현식(re) 사용법 - 05. 주석, 치환, 분리
파이썬 정규표현식(re) 사용법 - 06. 치환 함수, 양방탐색, 조건문
파이썬 정규표현식(re) 사용법 - 07. 예제(숫자)
파이썬 정규표현식(re) 사용법 - 08. 예제(단어, 행)
파이썬 정규표현식(re) 사용법 - 09. 기타 기능


이 글에서는 정규표현식으로 처리할 수 있는 예제를 설명한다.

본 글에서 정규표현식은 regex와 같이, 일반 문자열은 ‘regex’와 같이 표시하도록 한다.

파이썬 버전은 3.6을 기준으로 하나, 3.x 버전이면 (아마) 동일하게 쓸 수 있다.
2.7 버전은 한글을 포함한 비 알파벳 문자 처리가 다르다.


숫자

십진 정수

문제 1: 가장 간단한 십진 정수를 찾는 정규식을 작성하라.

문제 1 정답보기

r'\b[0-9]+\b'


문제 2: 가장 일반적인 형태의 십진 정수를 찾는 정규식을 작성하라. 맨 앞에 ‘+’ 혹은 ‘-‘ 기호가 붙어 있을 수 있으며, 기호와 숫자 사이에는 공백이 하나 있을 수 있다.

문제 2 정답보기

r'(?:[+-] ?)?\b\d+\b'

비 아스키 숫자를 포함하고 싶다면 \d를, 오로지 아라비아 숫자 10개만 숫자로 인식하게 하고 싶다면 [0-9]를 사용하도록 한다.
또한 문자열 전체에 일치되게 하고 싶다면 \A\Z를 사용하면 된다.


십진 정수의 leading-zero 제거

문제 3: 십진 정수의 앞에 오는 0들을 제거하는 정규식을 작성하라. 기호는 없다고 가정하자.

문제 3 정답보기

re.sub(r'\b0*([1-9][0-9]*|0)\b', r'\1', string)


leading-zero가 될 수 있는 부분과 그렇지 않은 부분을 구분하기만 하면 이 문제는 풀린다. 그리고 leading-zero를 제거하는 것이 목표이므로 캡처된 부분으로 re.sub 메서드를 쓰면 해결된다.

한 가지 주의점은 ‘0’ 또는 ‘00’ 등 진짜 0밖에 없는 경우이다. 이 경우 0을 하나 남겨두어야 하기 때문에, leading-zero 뒤 숫자 부분에서 0을 한 개 남겨 두었다(|0).


16진수

문제 4: 16진수를 찾는 정규식을 작성하라. 0-9의 숫자와 A-F 알파벳만 쓴다고 하자.

문제 4 정답보기

r'\A[0-9A-F]+\Z'


문제 5: 16진수를 찾는 정규식을 작성하라. 16진수는 아라비아 숫자 10개 또는 A-F, a-f를 사용하되 대문자와 소문자를 섞어 쓰지는 않는다. 또한, 16진수임을 나타내기 위해 숫자 바로 앞에 ‘0x’나 ‘&H’(hexa의 의미)를 붙이거나, 혹은 숫자 뒤에 ‘H’를 붙인다.

문제 5 정답보기

r'(\b0x|&H)?(?:[0-9A-F]+|[0-9a-f]+)(?(1)|H?)\b'


꽤 어려워 보일 것이다. 정규식은 복잡해지면 주석이 꼭 필요하다.

  1. (\b0x|&H)?: 보통 단어 경계를 맨 앞에 두는데, 이번엔 다자택일 중 하나에 두었다. 이유는 엠퍼샌드(‘&’)는 문자 집합에 포함되지 않으므로 단어 경계를 쓰면 안 된다.
    1. 앞에 16진수 식별자를 두는 것은 선택적이므로 ?를 쓴다.
    2. 그리고 이 부분은 첫 번째 캡처 그룹을 형성한다.
  2. (?:[0-9A-F]+|[0-9a-f]+): 16진수 숫자를 나타내는 부분만 빼고 싶다면 앞부분의 비 캡처 그룹임을 의미하는 ?:를 빼면 된다.
    1. 대소문자 혼용을 허용하지 않았으므로 대문자를 쓸 때와 소문자를 쓸 때를 분리하였다.
  3. (?(1)|H?): 조건문이다. 첫 번째 캡처 그룹에서 만약에 ‘0x’나 ‘&H’가 일치되었으면, 뒤에 또 ‘H’ 식별자를 두지는 않는다. 따라서 일치되었으면(‘맞으면’) 아무것도 없는 경우를, 일치되지 않았으면 ‘H’ 식별자를 선택적으로 일치시킬 수 있도록 한다.
  4. \b: 단어 경계이다.

참고: 지정된 범위의 숫자인지 확인

이는 정규식으로 작성하길 추천하지 않는다. 아니면 함수로 따로 빼 두는 것을 추천한다.
그냥 숫자인지만 정규식으로 확인하고, 주어진 범위인지는 다른 함수로 확인하자.

그래도 예를 하나 들어 보겠다. 얼마나 복잡한지를 느끼면 된다.

문제 6: 1 이상 365 이하의 정수인지를 확인하는 정규식을 작성하라.

문제 6 정답보기

r'(36[0-5]|3[0-5][0-9]|[12][0-9]{2}|[1-9][0-9]?)'


그냥 따로 씁시다.

number = re.search('\d+', '몇 이상 몇 이하의 수 38개')
number = int(number.group())

if 1 <= number <= 365:
    print('Yei!')

결과

Yei!

부동소수점 수

3번째 글의 끝에서 다음과 같은 문제가 있었다.

문제 7: 1.2이나 3.72e3, 1.002e-12 같은 수를 부동소수점 수 또는 과학적 표기법으로 표기한 수라고 한다. 이와 같은 수에 일치하는 정규표현식을 작성하라.

문제 7 정답보기

r'\b\d*\.\d+(e\d+)?'


조금 더 일반화한 버전을 문제로 하나 더 내 보았다.

문제 8 : 다음 조건에 맞는 부동소수점 수를 찾는 정규식을 작성하라.

  1. 부호부, 정수부, 가수부는 선택이다.
  2. 지수부 역시 선택이다.
  3. 정수부가 없으면 가수부는 필수이다.
  4. 가수부가 없으면 소수점은 선택이다.
문제 8 정답보기

r'[+-]?(\b[0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+\b)?'


정수부와 가수부가 동시에 없으면 안 되는 조건은 조건문을 사용해도 되고, 다자택일을 사용해도 된다. 이 경우는 다자택일을 사용했다.


로마 숫자

문제 9: 현대식 로마 숫자에 일치되는 정규식을 작성하라. ‘IV’는 가능하고, ‘IIII’는 불가능하다.

문제 9 정답보기

r'\b(?=[MDCLXVI])M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)\b'


  1. (?=[MDCLXVI]): 로마 숫자는 0이 없기 때문에, 문자가 하나라도 있는지 체크하는 전방탐색 구문이다.
  2. M*: 로마 숫자에서 1000은 개수만큼 집어넣는다.
  3. ` (C[MD] D?C*)`: 작은 숫자가 앞에 오는 경우를 따로 빼서 다자택일로 처리하였다. 로마 숫자의 규칙을 생각하면 그리 어려운 부분은 아니다.
  4. 나머지 부분은 3번과 같다.

다음 글에서는 단어와 행 처리에 관한 예제를 다루도록 하겠다.