이전 Hackerrank Detect Floating Point Number 문제에서 Regex를 사용하여 푸는것을 봤을 것이다:
import re
for _ in range(int(input())):
print(bool(re.match(r'^[-+]?[0-9]*\.[0-9]+$', input())))
이번에는 regex 표현에 대해 간략하게 알아보도록 하겠다.
※ regex 표현
이 정규 표현식을 사용하려면, 파이썬의 내장 함수에서 re를 import를 해와야 한다.
>>> import re
>>> p = re.compile('ab*')
다음, 이렇게 p=re.compile('ab*')는 'ab*'라는 정규표현식을 컴파일 하여, p는 이제 컴파일된 정규 표현식 객체가 되는 것이다. (즉, 특정 정규 표현식으로 운영되는 객체라는 것이다.)
이런 컴파일된 객체에는 4가지의 method가 존재한다:
Method | 목적 |
match() | 문자열의 처음부터 정규식과 매칭되는지 조사 |
search() | 문자열 전체를 검색하여 정규식과 매칭되는지 조사 |
findall() | 정규식과 매칭되는 모든 문자열을 리스트로 돌려줌 |
finditer() | 정규식과 매칭되는 모든 문자열을 반복 가능한 객체로 돌려줌 |
또한, 이 method로 생성된 객체에 대한 method 또한 존재한다:
Method | 목적 |
group() | 매칭된 문자열을 돌려줌 |
start() | 매칭된 문자열의 시작 위치를 돌려줌 |
end() | 매칭된 문자열의 끝 위치를 돌려줌 |
span() | 매칭된 문자열의 (시작, 끝)을 튜플의 돌려줌 |
다음의 예시를 보자:
>>> import re
>>> p = re.compile('[a-z]+')
p라는 컴파일된 정규 표현식 객체는, [a-z]+ 라는 정규표현식을 따르고 있다. (이 정규 표현식 규칙에 대해서는 밑에 더 자세히 설명하도록 하겠다.) 따라서 이 p라는 객체는 a또는 b또는 c또는 ... 또는 z중에 하나의 패턴이 1회 이상 반복해야된다는 뜻이다.
match:
만약 이 p객체에 match를 사용하여 출력을 해준다면:
>>> m = p.match("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'>
m이라는 객체를 돌려준다. 이를 만약 객체가 아닌 매칭된 문자열을 보고 싶다면:
>>> m.group()
'python'
위와 같은 모습을 볼 수 있다.
그렇다면 지금까지의 과정을 한번 살펴보자:
>>> import re
>>> p = re.compile('[a-z]+')
>>> m = p.match('python')
>>> m.group()
'python'
이렇게 p에 객체를 정의를 하고 우리는 시작했다. 하지만 다음과 같이 축약형을 사용하여 조금 더 간단하게 보여줄 수 있다:
>>> import re
>>> m = re.match('[a-z]+', 'python')
>>> m.group()
'python'
만약 컴파일된 객체를 여러번 사용할 것이 아닌 한번만 사용할 것이라면 이와 같이 축약된 형태로 사용하는 것도 하나의 방법이다.
search:
동일한 컴파일 정규 표현식 객체에서 사용해보자:
>>> m = p.search("python")
>>> print(m)
<re.Match object; span=(0, 6), match='python'>
>>> m = p.search("3 python")
>>> print(m)
<re.Match object; span=(2, 8), match='python'>
search('python')을 한다면 match('python')과 동일한 객체로 저장하는 것을 알 수 있다. 하지만 search('3 python')를 하게 된다면, 다른 객체를 저장하게 된다. 이유는 search() method는 위에 정의된 바와 같이 문자열을 처음부터 검색하는 것이 아니라 문자열 전체를 검색하기 때문에 '3'이 p에 없으므로 뒤의 'python'만 매칭되는 것이다. 따라서 search()로 정의한 객체 m도 다음과 같은 group을 가지고 있다:
>>> m = p.search('3 python')
>>> m.group()
'python'
findall:
findall의 경우 다음과 같은 예시를 들어보겠다:
>>> m = p.findall("python is good 3")
>>> print(m)
['python', 'is', 'good']
위의 match() 와 search() 처럼, p객체에 없는 문자열은 돌려주지 않고, findall()은 리스트 형태로 모두 돌려준다.
finditer:
>>> m = p.finditer('python is good 3')
>>> print(m)
<callable_iterator object at 0x00000216B3CC7520>
>>> for i in m:
print(i)
<re.Match object; span=(0, 6), match='python'>
<re.Match object; span=(7, 9), match='is'>
<re.Match object; span=(10, 14), match='good'>
findall 과 비슷하지만, iterator object(=반복 가능한 객체)를 돌려준다.
※ 정규식 패턴 표현
자주 사용되는 패턴들은 다음과 같다:
패턴 | 설명 | 예제 |
^ | 이 패턴으로 시작해야 한다. | ^python: python으로 시작해야함 |
$ | 이 패턴으로 종료되어야 한다. | python$: python으로 종료되어야 한다 |
[strings] | 문자들 중에 하나는 있어야 한다. | [cC]-language: C-language 또는 c-language |
[^strings] | 피해야 할 문자들의 집합을 정의 한다. | [^python]: p, y, t, h, o, n 은 없어야 함 |
| | Or 기능으로 둘 중에 하나여야 한다. | p | q: p또는 q여야 함 |
? | ?앞에 패턴이 없거나 하나여야 한다. | python?: python이 없거나 아니면 python 하나만 있어야 한다. (pythonpython 이렇게는 안된다.) |
+ | 1회 이상 반복 무조건 해야됨 | p+: p가 최소 하나는 있어야 한다 |
* | * 앞의 패턴이 0 이상이어야 함 | p*: p가 0 없거나 1개 이상이어 함 |
\d | 숫자 0-9 | \d: 0-9범위의 숫자가 1개 |
\w | 문자 | \w: 문자가 1개 |
\s | 화이트 스페이스 의미 | \s: \n, \t, \r 등 의미 |
. | \n을 제외한 모든 문자 의미 | .{2}: 문자 2개 의미 |
{n, m} | 앞선 패턴이 n번 이상 m번 이하 반복되어 나타남 | {2,3} 앞선 패턴이 최소 2번, 최대 3번 반복해서 나타난다. |
{n} | 앞선 패턴이 n번 반복해서 나타남 | {2} 앞선 패턴이 2번 반복해서 나타난다 |
그렇다면 이제 우리가 이전에 보았던 Hackerrank 문제를 보도록 하자
※ Hackerrank 문제
import re
for _ in range(int(input())):
print(bool(re.match(r'^[-+]?[0-9]*\.[0-9]+$', input())))
우선 정규 표현식을 사용하려면, 파이썬의 내장 함수에서 re를 import 해와야 한다.
또한, bool()을 통해 True/False를 나타내는 자료형을 만들어 주고, 그 안에 input()을 넣는다.
re.match()는 다음과 같은 형태를 띄고 있다:
re.match('정규 표현식', '문자열 source')
즉, 위의 문제에서 re.match(r'^[-+]?[0-9]*\.[0-9]+$, input())에서 앞의 부분은 어떤 식으로 표현될 것인지 알려주는 정규 표현식, 뒤의 input()이 문자열의 source가 되는 것이다. 이제 이 풀이의 정규식 표현을 한번 알아보도록 하겠다:
- 초반의 r은 raw_string을 나타내는 것으로 escape 표현인 \n 이나 \b 같은 표현이 안 나타나게끔 한다
- ' ' 사이에 있는 것이 우리의 정규식 표현이다
- ^[-+]: - 또는 + 가 우선적으로 나와서 시작되어야 한다
- ^[-+]?: 시작됨과 동시에, 이 패턴이 있거나, 없어야 한다. (-+ 둘다 같이 못 있는다는 뜻이다)
- [0-9]: 0 부터 9까지의 문자들 중 하나라는 뜻이다
- [0-9]*: 이 0부터 9까지의 문자들 중 0개 이상 있어야 한다는 뜻이다 (없을 수도 있다는 뜻이다)
- \. : ' . ' 이라는 문자(단순 점)가 포함되어 있어야 한다
- [0-9]+$: 0부터 9까지의 패턴이 최소 하나 이상이어야 하고, 이 패턴으로 끝마친다.
라는 뜻이다. 이렇게 쓴다면 Hackerrank Detect Floating Point Number 문제에서 요구하는 모든 요구사항들을 알맞게 들어주는 것이다.
Regex 정규 표현식을 보면 매우 복잡하고 어려워 보인다. 하지만 적응한다면 매우 괜찮은 표현일 것이라고 생각된다.
(이 정리는 점프 투 파이썬에도 자세히 나와 있다. 이를 참고하면서 정리를 했다.)
'Python 공부하기' 카테고리의 다른 글
zip 내장 함수에 대하여 (0) | 2020.11.27 |
---|---|
isdecimal(), isdigit(), isnumeric()에 대하여 (0) | 2020.11.25 |
Enumerate 함수에 대하여 (0) | 2020.11.25 |
super - 기반 class / 상속자(Constructor) / *args, **kwargs 알아보기 (2) | 2020.11.08 |
Methods에 대한 고찰 (__init__, if __name__ == "__main__") (0) | 2020.10.23 |