공부 목적으로 정리한 글이며 다른 분들의 포스트를 상당히 참고했습니다
정규표현식 Regular Expression은 줄여서 regex라고 부르기도 한다.
특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식이다.
파이썬 뿐 아니라 다른 언어에서도 사용하며, 활용성이 매우 높다.
.^$*+?\|(){}[]
[abc] # a 또는 b 또는 c
-
문자는 [ ] 안에서 문자 사이의 범위를 의미한다. () 소괄호 안에서는 문자 '-' 그대로의 의미이다.[a-c] # a부터 c까지
[a-zA-Z] # 모든 알파벳
[abc] # 동일한 결과
^
문자는 ^
뒤의 문자로 시작하면 문자와 매치된다. ^a # a로 시작하는 문자
aaa # 매치
baa # 매치되지 않음
^
문자는 [ ] 안에서 반대를 의미한다.[^a-c] # a부터 c까지를 제외한 문자
.
문자는 두 문자 사이에 줄바꿈 문자인 \n
를 제외한 모든 문자와 매치된다.
점 하나 당 하나의 문자이다. a..b
이면 a와 b 사이에 문자 2개가 있는 경우를 의미한다. '.' 문자 그대로 사용하려면 '.' 로 사용해야 한다.
a.b # a와 b 사이에 \n를 제외한 모든 문자
aab # a와 b 사이에 a는 모든 문자에 포함됨으로 매치
abc # a와 b 사이에는 문자가 없음으로 매치되지 않음
.
문자는 [ ] 안에서 원래 의미인 마침표를 의미한다.a[.]b # a와 b 사이에 마침표가 있으면 매치
a.b # a와 b 사이에 마침표가 있음으로 매치
a0b # a와 b 사이에 마침표가 없음으로 매치되지 않음
*
문자는 앞 문자가 0개 이상일 때 매치된다.ab*c # a와 c 사이에 b가 0개 이상인 문자
ac # a와 c 사이에 b가 0개임으로 매치
abc # a와 c 사이에 b가 1개임으로 매치
adc # a와 c 사이에 d가 1개임으로 매치되지 않음
+
문자는 앞 문자가 1개 이상일 때 매치된다.ab*c # a와 c 사이에 b가 1개 이상인 문자
ac # a와 c 사이에 b가 0개임으로 매치되지 않음
abbbc # a와 c 사이에 b가 3개임으로 매치
adc # a와 c 사이에 d가 1개임으로 매치되지 않음
?
문자는 앞 문자가 0~1개일 때 매치된다.ab?c # a와 c 사이에 b가 0~1개인 문자
ac # a와 c 사이에 b가 0개임으로 매치
abbbc # a와 c 사이에 b가 3개임으로 매치되지 않음
adc # a와 c 사이에 d가 1개임으로 매치되지 않음
{m}
문자는 앞 문자가 m번 반복될 때 매치된다.ab{3}c # a와 c 사이에 b가 3개인 문자
ac # a와 c 사이에 b가 0개임으로 매치되지 않음
abbbc # a와 c 사이에 b가 3개임으로 매치
abbbbbc # a와 c 사이에 b가 5개임으로 매치되지 않음
{m, n}
문자는 앞 문자가 m번에서 n번까지 반복될 때 매치된다.ab{3, 5}c # a와 c 사이에 b가 3~5개인 문자
ac # a와 c 사이에 b가 0개임으로 매치되지 않음
abbbc # a와 c 사이에 b가 3개임으로 매치
abbbbbbc # a와 c 사이에 b가 6개임으로 매치
|
문자는 여러 개의 정규표현식을 or로 구분하며, 이중 하나와 매치된다.a|b|c # a 또는 b 또는 c인 문자
ac # 매치
b # 매치
d # 매치되지 않음
$
문자는 $
문자 앞의 문자로 끝나면 매치된다.
여러 문자열인 경우 마지막 줄만 적용되지만, re.MULTILINE 옵션을 사용하면 각 줄에 적용된다.
a$ # a로 끝나는 문자
aaa # 매치
aab # 매치되지 않음
\A
문자는 ^
와 동일하지만 re.MULTILINE 옵션을 무시하고 항상 문자열 첫 줄의 시작 문자를 검사한다. \Z
는 $
와 동일하지만 re.MULTILINE 옵션을 무시하고 항상 문자열 마지막 줄의 끝 문자를 검사한다.\A
문자는 ^
와 동일하지만 re.MULTILINE 옵션을 무시하고 항상 문자열 첫 줄의 시작 문자를 검사한다. \Z
는 $
와 동일하지만 re.MULTILINE 옵션을 무시하고 항상 문자열 마지막 줄의 끝 문자를 검사한다.\
사용하기\
을 표현하는 방법은 2가지가 있다.re.compile('\\\\string', '\string') #가독성이 떨어지니 2번째 방법을 자주 사용한다
re.compile(r'\\string', '\string')
'hello(?=world)' # hello 뒤에 world가 있으면 hello 매치
helloworld # 매치
byeworld # hello가 없음으로 매치되지 않음
hellohello # hello 뒤에 world가 없음으로 매치되지 않음
'hello(?!world)' # hello 뒤에 world가 없으면 hello 매치
helloworld # 매치되지 않음
byeworld # hello가 없음으로 매치되지 않음
hellohello # hello 뒤에 world가 없음으로 매치
'(?<=hello)world' # world 앞에 hello가 있으면 world 매치
helloworld # 매치
byeworld # hello가 없음으로 매치되지 않음
worldworld # world 앞에 world가 없음으로 매치되지 않음
'(?<!hello)world' # world 앞에 hello가 없으면 world 매치
helloworld # 매치되지 않음
byeworld # hello가 없음으로 매치
worldworld # world 앞에 world가 없음으로 매치
re
모듈을 통해 정규표현식을 사용import re
re.compile()
을 통해 정규표현식을 컴파일 하여 변수에 저장한 후 사용할 수 있다._sre.SRE_Pattern
패턴 클래스 객체이다.r = re.compile('정규표현식')
print(type(r))
re.DOTALL
혹은 re.S
옵션을 넣어주면 .
을 사용했을 때 \n
또한 포함하여 매칭한다.rg = re.compile('.')
rs = rg.findall('12\n34')
print(rs)
옵션을 추가하지 않으면 \n
은 매칭되지 않는다.
rg = re.compile('.', re.DOTALL)
rs = rg.findall('12\n34')
print(rs)
옵션을 추가하면 \n
또한 매칭된다.
re.IGNORECASE
혹은 re.I
옵션을 넣어주면 대소문자를 구별하지 않고 매칭한다.re.compile('[a-z]', re.IGNORECASE) # 알파벳 대소문자 모두 매칭
re.MULTILINE
혹은 re.M
옵션을 넣어주면 앞 혹은 뒤의 문자를 확인하는 ^
와 $
를 여러 줄의 문자열에도 적용 가능하게하여 매칭한다.re.VERBOSE
혹은 re.X
옵션을 넣어주면 정규표현식에 공백과 코멘트를 추가하여 더욱 가독성 좋게 표현식을 작성할 수 있다.
[]
내의 공백은 제외
re.compile(r"""
[a-z] #알파벳 소문자
""", re.VERBOSE)
r = re.compile('[a-z]+') # 알파벳이 하나 이상 반복되는 지
print(r.match('abcabc'))
print(r.match('123abc'))
print(r.match('abc123'))
# 또는
print(re.match('abc', 'abcabc'))
print(r.search('abcabc'))
print(r.search('123abc'))
print(r.search('123abc'))
# 또는
print(re.search('abc', 'abcabc'))
print(r.findall('123abc456def'))
# 또는
print(re.findall('abc', 'abcabc'))
finditer 매서드는 문자열 전체에서 일치하는 모든 패턴을 찾아 반복가능 객체로 반환한다.
반환타입은 callable_iterator 이다. 반복가능한 객체의 각 요소는 match 객체이다.
fitr = r.finditer('1a2b3c4d')
print(type(fitr))
for i in fitr:
print(i)
text = re.sub('찾을 패턴', '패턴 변경할 내용', '원본')
<_sre.SRE_Match object; span=(매치 시작지점 인덱스, 매치 끝지점 인덱스), match='매치된 문자열'>
fitr = r.finditer('1a2b3c4d') for i in fitr: print(i. group(), i.start(), i.end(), i.span())
위의 표현식에 대한 문자들은 하나의 문자에만 적용되어 불편함이 있다.
예를 들어 '12'가 반복되는 '12', '1212', '121212' 와 같은 문자를 매칭하고 싶지만 '12+'는 '122', '12222'와 같이 문자 '2'에만 적용되어 반복된다.
re.findall('12+','12221212')
re.match('(12)+','1212')
groups()
매서드를 사용하면 그룹들을 튜플 형태로 리턴할 수 있다. group()
매서드를 사용하면 각 그룹에 대한 매치 결과를 얻을 수 있다.pp = re.match('(12)...(de)', '12abcde67') grouping = pp.groups() print(grouping)
print(pp.group(), # 전체 매치 결과 pp.group(0), # 전체 매치 결과와 동일 pp.group(1), # 첫번째 그룹 매치 결과 pp.group(2)) # 두번째 그룹 매치 결과
❓ 의문인 점: 'de' 그룹으로 매칭할 때 match 함수라면 none 결과가 나와야 하는 것 아닌지..?
re.findall('(12)+','12221212')
for i in re.finditer('(12)+','12221212'): print(i)
(?P<그룹이름>표현식)
형식을 사용해야 한다.b = re.search('(?P<f>12)(?P<s>34)', '1234') print(b.group('f'), b.group('s'))
re.search('(?P<f>12)(?P<s>34)..(?P=f)(?P=s)', '1234561234')
\그룹번호
를 사용해 그룹을 재참조할 수 있다. 표현식 앞에 r
을 붙여야 한다.re.search(r'(?P<f>12)(?P<s>34)..\1\2', '1234561234')
print(re.findall('\d{4}-(\d\d)-(\d\d)', '2028-07-28')) print(re.findall('\d{4}-(\d\d)-(\d\d)', '1999/05/21 2018-07-28 2018-06-31 2019.01.01'))
a = re.search('((12)+)','aaa121212') print(a) print(a.group(1)) print(a.group(2))
비캡처 그룹은 (?:<regex>)
와 같이 사용한다.
비캡처 그룹은 재참조가 불가하다.
소괄호 뒤에 물음표가 붙는 (?s)
와 같은 모드 변경자 또한 캡처 그룹으로 작동하지 않는다.
a = re.search('((?:12)+)','aaa121212') print(a) print(a.group(1)) print(a.group(2))
두번째 그룹은 없다는 오류가 발생한다. (비 캡처 그룹으로 지정했기 때문)