정규표현식
문자열의 규칙을 찾아서 어떤거와 일치하는 것을 뭐로 바꿔라~ --> 이런 문제를 처리할때 주로 사용
- match
import re
p=re.compile('[a-z]+')
# a부터 z까지의 문자열이 한번이상 반복되는 표현식을 찾아라
m = p.match('python')
print(m) # match가 된다
m2=p.match('3 python')
print(m2) # 3은 p에 들어있는 표현이 아니므로 매치가 되지 않는다
match가 되는 경우는
<re.Match object; span=(0, 6), match='python'>
이러한 문구가 나오고
match가 되지 않는 경우는
None
이라고 나온다
- search
s=p.search('3 python')
print(s)
하지만 search의 경우 3이 들어있더라도 그 뒤의 python은 p와 매치가 되기 때문에 값이 나온다.
<re.Match object; span=(2, 8), match='python'>
이런 문구가 나오게 된다.
- findall
fa=p.findall('life is too short')
print(fa)
findall의 경우 일치하는 string을 리스트에 담아서 출력해준다.
['life', 'is', 'too', 'short']
- finditer
fi=p.finditer('life is too short')
print(fi)
for r in fi:
print(r)
print(fi)를 하게 되면 <callable_iterator object at 0x7ff347e5a910> 다음과 같은 문구가 나오게 되고
이것을 for문을 통해 확인을 해보면 각각의 match되는 string들이 match 객체의 형태로 나오는 것을 알 수 있다.
print(r)의 결과는
<re.Match object; span=(0, 4), match='life'>
<re.Match object; span=(5, 7), match='is'>
<re.Match object; span=(8, 11), match='too'>
<re.Match object; span=(12, 17), match='short'>
이렇게 나온다
- match 객체의 메서드
- .group() : 매치된 문자열을 리턴한다
- .start() : 매치된 문자열의 시작 위치를 리턴한다
- .end() : 매치된 문자열의 끝 위치를 리턴한다
- .span() : 매치된 문자열의 (시작, 끝)에 해당되는 튜플을 리턴한다
import re
p=re.compile('[a-z]+')
m=p.match('python') # m이 match의 객체가된다.
print(m.group())
print(m.start())
print(m.end())
print(m.span())
print(m.group()) # match된 문자열이 나온다
--> python
print(m.start()) # 처음 시작되는 index
--> 0
print(m.end()) # 끝의 index
--> 6
print(m.span()) # 시작과 끝을 튜플의 형태로 리턴
--> (0, 6)
- compile 옵션
- DOTALL, S (DOTALL의 약어)
- Dot(.) : 줄바꿈(\n)을 제외한 모든 문자와 매치
p = re.compile('a.b')
m = p.match('a\nb')
print(m)
## None
## 줄바꿈 문자와 매치가 되지 않기 때문에 결과가 나오지 않는다.
p=re.compile('a.b',re.DOTALL)
m = p.match('a\nb')
print(m)
## <re.Match object; span=(0, 3), match='a\nb'>
## DOTALL이란 옵션을 사용하여 줄바꿈을 포함한 모든 문자와 매치가 되도록 한다
- IGNORECASE, I
p=re.compile('[a-z]+')
print(p.match('python'))
## 매치가 된다
## <re.Match object; span=(0, 6), match='python'>
print(p.match('Python'))
## 앞의 P가 대문자이기 때문에 매치가 안된다
## None
print(p.match('PYTHON'))
## None
하지만 compile 옵션인 .IGNORECASE 또는 .I를 사용하면 대문자와 소문자 문제를 해결할 수 있다.
p=re.compile('[a-z]+',re.I)
print(p.match('python'))
print(p.match('Python'))
print(p.match('PYTHON'))
<re.Match object; span=(0, 6), match='python'>
<re.Match object; span=(0, 6), match='Python'>
<re.Match object; span=(0, 6), match='PYTHON'>
- MULTILINE, M
p=re.compile("^python\s\w+")
## 맨 앞글자(^)가 python
## \s : 공백을 나타내는 문자
## \w : 알파벳, 숫자, _ 중의 한 문자
## + : \w 단어가 반복되는
data="""python one
life is too short
python two
you need python
python three"""
print(p.findall(data))
## 이 결과로는 data라는 멀티라인의 맨 앞에 있는 'python one'이 출력된다.
p=re.compile("^python\s\w+",re.M)
data="""python one
life is too short
python two
you need python
python three"""
print(p.findall(data))
## ['python one', 'python two', 'python three']
멀티라인 각각을 새로운 한 줄로 인식해주므로 각 라인의 가장 앞에 python 이 있으면 출력해준다.
- VERBOSE, X
charref = re.compile(r'&[#](0[0-7]+|[0-9]+|x[0-9a-fA-F]+);')
charref = re.compile(r"""
&[#] # Start of a mumeric entity reference
(
0[0-7]+ # Octal form
| [0-9]+ # Decimal form
| x[0-9a-fA-F]+ # Hexadecimal form
)
; # Trailing semicolon
""", re.VERBOSE)
긴 정규 표현식이 있을 때 VERBOSE를 사용하여 나눠 쓸 수가 있다.
- 백슬래시 문제
p = re.compile('\\section')
# 백슬래시를 하고자 할때는 \\ 2개를 넣어준다.
# 근데 \\는 \로 문자열이 바뀌게 된다.
p = re.compile('\\\\section')
# 따라서 \\ 이렇게 2개를 넣고 싶을때는
# \\\\ 4개를 넣어야 2개가 된다.
p = re.compile(r'\\section')
# \\\\ 이렇게 4개를 넣는게 불편하므로 앞에 r을 붙여주어 raw string을 해주면
# \\ 2개만 해도 그대로 인식해준다.
- 메타문자
- | : or의 의미 - 앞에꺼 또는 뒤에꺼와 일치할때를 의미
p = re.compile('Crow|Servo')
m = p.match('CrowHello')
print(m)
위의 코드에서 p객체는 Crow 또는 Servo를 나타내고 m에는 Crow가 있으므로 앞의 Crow와 match가 된다
- ^ : 첫 글자를 의미
print(re.search('^Life', 'Life is too short'))
# Life is too short에서 Life가 맨 처음으로 나왔기 때문에 매치가 되어 나왔다
print(re.search('^Life', 'My Life'))
# 처음 글자가 Life여야 하는데 My가 나왔기 때문에 매치가 되지 않았다
- $ : 끝 글자를 의미
print(re.search('short$', 'Life is too short'))
# Life is too short에서 short가 맨 뒤에 나왔기 때문에 매치가 되어 나왔다
print(re.search('short$', 'Life is too short, you need python'))
# 맨 끝 글자가 short 여야 하는데 python이 나왔기 때문에 매치가 되지 않았다
- \b : 공백을 나타내는 메타문자
p = re.compile(r'\bclass\b')
# class의 양 옆에 공백이 있다
print(p.search('no class at all')
# class의 양 옆에 공백이 있으므로 match가 된다
print(p.search('the declassified algorithm'))
# class가 있긴 하지만 양옆에 공백이 없으므로 match가 되지 않는다
print(p.search('one subclass is'))
# class가 있긴 하지만 양옆에 공백이 없으므로 match가 되지 않는다
- 그루핑 ( )
p = re.compile('(ABC)+')
# 만일 (ABC)+로 그루핑을 하지 않고 ABC+ 였다면 C만 반복되는 것을 찾게 될 것이다.
# 우리가 원하는건 ABC가 그룹이 되어 반복되는 것을 원하므로 그루핑을 해줘야 한다.
m = p.search('ABCABCABC OK?')
print(m)
print(m.group())
p = re.compile(r"(\w+)\s+\d+[-]\d+[-]\d+")
m= p.search("park 010-1234-1234")
print(m.group(1))
# 그루핑을 했을 때 제일 처음 그룹(group(1)) 을 가져오게 된다.
# park 을 가져온다
p = re.compile(r'(\b\w+)\s+\1')
# \1 의 뜻은 grouping 된 표현이 있을때, 이 결과를 \1의 자리에 써준다
print(p.search('Paris in the the spring').group())
# 이걸 실행하면
# the the 가 나온다 이게 이 문장에서 바로 뒤에 반복되는 단어가 있다는 것을 알려준다
p = re.compile(r"(?P<name>\w+)\s+\d+[-]\d+[-]\d+")
# ?P<name> 을 통해 그룹에 이름을 붙여준다
m= p.search("park 010-1234-1234")
print(m.group("name"))
# 그루핑을 했을 때 name이라는 그룹의 이름이 속한 값을 가져오게 된다.
# park 을 가져온다
?P<name> 을 통해 그룹에 이름을 붙여준다
- 전방탐색 : 긍정형 (?=)
p = re.compile(".+:")
# 문자열이 반복되다가 : 을 만날때까지를 매칭시킨다
m = p.search("http://google.com")
print(m.group())
# 결과로는
# http:
근데 http: 이 아니라 http 만 가지고 오고 싶다!!
이때 사용하는 것이 긍정형
p = re.compile(".+(?=:)")
# : 이 올 자리에 (?=이 자리에 :을 넣어준다
# 즉 검색조건에는 포함되지만 결과에는 포함되지 않는다
m = p.search("http://google.com")
print(m.group())
# 결과는
# http
- 전방탐색 : 부정형 (?!)
p = re.compile(".*[.](?!bat$).*$", re.M)
# .* 어떤 문자열이 쭉~ 있고
# [.] 그 문자열 뒤에 .이 온 다음에
# 그 뒤에 확장자가 올 텐데 . 뒤에 오는 확장자 중에 bat가 끝인 것은 포함되지 않게 해달라
l = p.findall("""
autoexec.exe
autoexec.bat
autoexec.jpg
""")
print(l)
이것을 실행하면 ['autoexec.exe', 'autoexec.jpg'] 이렇게는 포함되지만 확장자가 .bat인 것은 포함되지 않는다.
만약 .exe도 포함되지 않게 하려면??
p=re.compile(".*[.](?!bat$|exe$).*$",re.M) 이렇게 or를 나타내는 | 을 이용하여 exe를 추가시켜준다.
- 문자열 바꾸기 sub
p = re.compile('(blue|white|red)')
p.sub('colour', 'blue socks and red shoes')
# p라는 객체에 들어있는 blue white red 라는 단어를 colour로 바꿔준다.
# 이것을 시행하면
# colour socks and colour shoes 이런 문장이 되어 나온다
- Greedy vs Non-Greedy
s = '<html><head><title>Title</title>'
print(re.match('<.*>',s).group()) # Greedy
#< > 사이에 문자열이 들어가게 해서 이것과 일치하는 것을 출력
# 결과는
# <html><head><title>Title</title> 전체 < > 를 인식하여 길게 다 가져온다
print(re.match('<.*?>',s).group()) # Non-Greedy
# <.*?> ?를 넣어줌으로써 최소한의 반복으로 매칭이 되도록 해준다.
# 결과는
# <html> 이 나온다
'코딩' 카테고리의 다른 글
PANDAS 자주 쓰고 기초적인 코딩 (0) | 2021.08.11 |
---|---|
프로그래머스 - 코딩테스트(완주하지 못한 선수) (0) | 2021.01.14 |
프로그래머스 - 코딩테스트 연습(두 개 뽑아서 더하기) (0) | 2021.01.13 |