[Python] 정규 표현식(3) : 핵심 모음
정규식 관련된 문제를 풀다보니 내가 모르고 있는 부분이 많다는 느꼈다. 그래서 정규식을 잘 활용할 수 있는 기능들을 이 곳에 모아 정리해보고자 한다!
👩 Anchors: 문자열의 시작(^)과 끝($)
^Hello Hello로 시작하는 모든 문자열을 매칭
Bye$ Bye로 끝나는 모든 문자열과 매칭
^Hello Bye$ 정확히 Hello Bye와 일치하는 문자열과 매칭
👩 Quantifier: 수량자(+*?{})
asd+ as와 1개 이상의 d를 포함한 문자열과 매칭
asd* as와 0개 이상의 d를 포함한 문자열과 매칭
asd? as와 0개 혹은 1개의 d를 포함한 문자열과 매칭
a{2}sd 2개의 a 그리고 sd를 포함한 문자열과 매칭
asd{3, } as와 3개 이상의 d를 포함한 문자열과 매칭
asd{1, 4} as와 1개 이상 4개 이하의 d를 포함한 문자열과 매칭
a(sd)+ a와 1개 이상의 sd를 포함한 문자열과 매칭
👩 or: | & []
[as]d a 또는 s 그리고 d를 포함한 문자열과 매칭
(a|s)d 위와 동일
👩 특수문자 그대로 사용하기: \
특수문자^.[$(|*+?{\
를 그대로 사용하기 위해서는 문자 앞에 \(역슬래시)
를 붙여 사용한다.
'$\d' 숫자로 시작하는 문자열과 매칭
'\$\d' $문자와 숫자 하나를 포함하는 문자열과 매칭
👩 플래그
re.I | re.IGNORECASE 알파벳 문자에 대해 대소문자를 구분하지 않는다.
re.M | re.MULTILINE 문자열 전체가 아니라 줄 별로 패턴을 매칭한다.
👩 Grouping & Capturing: ()
소괄호는 캡쳐 그룹을 생성한다. 그렇다면 캡쳐 그룹은 어떤 때 쓰이는 것일까?
str = "my computer, my mouse, my speaker, my camera, my audio"
print(re.findall('my ([a-z]+)', str))
print(re.findall('my [a-z]+', str))
캡쳐 그룹을 사용하는 것과 사용하지 않는 것의 차이를 확인해보자!!
re.findall('my ([a-z]+)', str) ['computer', 'mouse', 'speaker', 'camera', 'audio']
re.findall('my [a-z]+', str) ['my computer', 'my mouse', 'my speaker', 'my camera', 'my audio']
- 캡쳐 그룹을 통해서
my _물건_
중 물건들을 뽑아 올 수 있다. - 캡쳐를 사용하지 않는다면 my 그리고 1개 이상의 소문자를 포함한 문자열과 매칭된 결과물이 반환된다.
🙄 그렇다면 캡쳐를 하지 않고 단순히 그룹 형태의 패턴을 이용하고 싶다면 어떻게 해야 할까??
a(sb) 캡쳐 그룹을 생성한다.
a(?:sb) 캡쳐 그룹 생성을 무시한다.
a(?<name>bc) 캡쳐 그룹에 이름을 지정하고 ${name}을 통해 사용할 수 있다.
- 캡쳐된 문자열들은 index를 통해 사용할 수 있다.
- 그룹 이름을 지정했다면 해당 이름을 통해 사용할 수 있다.
👩 범위: []
[asb] a 또는 s 또는 b를 포함하는 문자열과 매칭
[a-z] 알파벳 소문자 하나와 매칭
[0-9]% 0 이상 9 이하 숫자 하나와 %를 포함한 문자열과 매칭
[a-zA-Z] 대소문자 상관없이 알파벳 하나와 매칭
[^a-zA-Z] 영문이 아닌 문자와 매칭
^
는 부정표현으로 사용된다.- [] 안에서는 특수문자가 적용하지 않으므로 특수문자를
\
없이, 즉 excape시키지 않고 그냥 사용할 수 있다.
👩 Greedy & Lazy match: ?
보통 +
, *
, {}
는 가능한 최대한 많이 매칭하려고 한다. 다음의 예를 살펴보자!
str = "<computer> <mouse> <speaker> <camera> <audio>"
print(re.findall('<.+>', str)) # ['<computer> <mouse> <speaker> <camera> <audio>']
print(re.findall('<.+?>', str)) # ['<computer>', '<mouse>', '<speaker>', '<camera>', '<audio>']
나는 제일 처음 <>마다 들어있는 단어를 확인하고 싶지만 위와 같이 정규식을 작성할 경우 다음과 같은 결과가 나온다.
즉, 가능한 최대한 많이 매칭되기 때문에 첫 번째 괄호에서 마지막 괄호까지 모든 문자열이 매칭되는 것이다. 이를 ?
를 통해서 해결할 수 있다.
정리하면 다음과 같다!!
<.+> <> 사이 가능한 모든 문자와 매칭된다.
<.+?> <> 사이 하나 이상의 문자와 매칭되고, 가능한 짧게 매칭된다.
👩 단어 경계 메타 문자: \b & \B
asdfasbdsf as efefedsas aasa
여기서 \b
는 단어의 경계 위치를 가리킨다. 위 문자에서 \b
는 |asdfasbdsf| |as| |efefedsas| |aasa|
이다.
\B
는 \b와 반대인 a|s|d|f|a|s|b|d|s|f a|s e|f|e|f|e|d|s|a|s a|a|s|a
가 된다.
'\ba[a-z]+\b' (asdfasbdsf) (as) efefedsas (aasa)
'\Ba[a-z]+\B' asdf(asbds)f as efefedsas a(as)a
👩 역참조: \1
'([asb])\1' '\1'은 첫 번째 캡쳐 그룹과 동일한 패턴을 의미한다. => '([asb])([asb])'
'([asb])([qwe])\2\1' '\2'는 두 번째 캡쳐 그룹과 동일하고 '\1'은 첫 번째 캡쳐 그룹과 동일하다.
'(?<name>[a-z]+)\k<name>' '\k<name>'은 캡쳐 그룹 name과 동일한 패턴을 매칭한다.
👩 Lookaround: Lookahead(?=), Lookbehind(?<=)
Lookahead
는 꼬릿말을 확인하는 용도이고, Lookbehind
는 머릿말을 확인하는 용도이다!
a(?=b) b가 바로 뒤에 있는 a와 매칭된다. 여기서 b는 포함되지 않는다.
(?<=a)b a가 바로 앞에 있는 b와 매칭된다. 여기서 a는 포함되지 않는다.
여기에 부정을 합칠 수 있다.
a(?!b) b가 바로 뒤에 없는 a와 매칭된다. 여기서도 b는 포함되지 않는다.
(?<=a)b a가 바로 앞에 없는 b와 매칭된다. 여기서도 a는 포함되지 않는다.
📃 참고
- https://chrisjune-13837.medium.com/%EC%A0%95%EA%B7%9C%EC%8B%9D-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EC%98%88%EC%A0%9C%EB%A5%BC-%ED%86%B5%ED%95%9C-cheatsheet-%EB%B2%88%EC%97%AD-61c3099cdca8
- https://dogcowking.tistory.com/230
- https://blog.naver.com/PostView.naver?blogId=hankrah&logNo=222214837400&categoryNo=65&parentCategoryNo=0&viewDate=¤tPage=1&postListTopCurrentPage=1&from=postList
- https://ohgyun.com/392
댓글남기기