Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
Tags
- 약수 수하기
- aw3
- 파이썬 #python #지역함수
- 파이썬 #python #모듈 #module #import #random #time #calendar #sys
- 파이썬 #python #lambda #람다
- spring boot
- Git
- PostgreSQL
- 파이썬기본문법 #파이썬 #python
- 파이썬 #python #filter #map #reduce
- 파이썬 #python #class #클래스 #상속
- 파이썬 #python #enumerate
- docker
- 연산자메서드
- 파이썬 #python #가변매개변수 #키워드가변매개변수 #args #kwargs
- 파이썬 #python #docstring
- jsonb
- 배포
- 프로그래머스
- 파이썬 #python #함수 #function
- 파이썬 #python #file #i/o #input #output
- 파이썬 #python #전역변수 #지역변수 #eval
- 파이썬 #python #예외처리 #exception
- EC2
- 파이썬 #python #Comprehension
- redis
- aws
- 민감 정보 관리
- 사용자정의예외
- 파이썬 #python #os #os.path #glob
Archives
- Today
- Total
Yeonnnnny
정규 표현식 part3 본문
■ 파이썬에서 정규 표현식 사용
- re 모듈 사용
re 모듈의 compile 함수를 이용하여 정규 표현식을 컴파일함
import re
p = re.compile('[a-z]+')
- 정규식을 사용한 문자열 검색
컴파일 된 객체를 아래의 함수를 이용하여 문자열 검색을 할 수 있음
match() | 문자열의 처음 시작부터 검색하여 일치하지 않는 부분이 나올 때까지 찾음 |
search() | 문자열 전체를 검색하여 처음으로 매치되는 문자열 찾음 |
findall() | 정규식과 매치괴는 모든 문자열을 찾아 리스트로 반환 |
finditer() | 정규식과 매치되는 모든 문자열을 반복 가능한 객체로 반환 |
★ 컴파일과 매치 동시 수행
m = re.match( '[a-z]+' , 'python' )
□ match()
import re
p =re.compile('[a-z]+') # 알파벳 소문자가 1개 이상인지
m1= p.match('python')
print(m1) # <re.Match object; span=(0, 6), match='python'>
m2 = p.match('pYthon')
print(m2) # <re.Match object; span=(0, 1), match='p'>
m3 = p.match('Python')
print(m3) # None
m4 = p.match('3 python')
print(m4) # None
※ 매치하고자 하는 문자열과 패턴을 처음부터 비교
□ search()
import re
p =re.compile('[a-z]+') # 알파벳 소문자가 1개 이상인지
m=p.search('3 python')
print(m) # <re.Match object; span=(2, 8), match='python'>
print(m.group()) #
※ 처음부터 매치되지 않아도 문자열 전체를 검색하여 매치되는 문자열이 있으면, 그 결과를 반환함
○ [문제] 전화번호 추출하기 : 다음의 전화번호 데이터에서 전화번호만 추출하는 정규표현식 작성
import re
phone =['홍길동:010-1234-5678','우리집:02-5555-3333']
# 1)
p=re.compile('\d{2,3}-\d{3,4}-\d{4}')
for i in phone:
print(p.search(i).group())
# 결과
010-1234-5678
02-5555-3333
□ findall()
import re
pattern = re.compile('[a-z]+')
result=pattern.findall('life is too short !')
print(result)
# 결과
['life', 'is', 'too', 'short']
□ finditer()
import re
pattern = re.compile('[a-z]+')
result=pattern.finditer('life is too short !')
for i in result:
print(i)
# 결과
<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.search('python')
print(m.group())
print(m.start())
print(m.end())
print(m.span())
# 결과
python
0
6
(0, 6)
■ 컴파일 옵션
옵션 | 약어 | 설명 |
DOTALL | S | dot(.) 메타 문자가 줄바꿈 문자를 포함하여 모든 문자와 일치하도록 함 |
IGNORECASE | I | 대소문자에 관계엾이 일치 |
MULTILINE | M | 여러 줄의 문자열에 대해 ^, $ 메타문자를 적용할 수 있음 ※ ^ 는 문자열의 처음을, $ 는 문자열의 마지막을 의미 |
VERBOSE | X | 정규식을 보기 편하게 만들고 주석 등을 사용할 수 있게 함 |
□ DOTALL
# DOTALL
import re
m = re.match('a.b', 'a\nb')
print(m) # None : dot에는 \n(개행문자) 포함 X
p = re.compile('a.b',re.DOTALL)
m = p.match('a\nb')
print(m) # <re.Match object; span=(0, 3), match='a\nb'> : 매치됨
s = '''hello
python'''
# 사용X
p = re.compile('hello.python')
m = p.match(s)
print(m) # None
# 사용O
p = re.compile('hello.python',re.DOTALL)
m = p.match(s)
print(m) # 매치 성공
□ IGNORECASE
# IGNORECASE
# 사용 X
p = re.match('[a-z]+','pYthon')
print(p)
# 사용 O
p = re.compile('[a-z]+',re.IGNORECASE)
m = p.match('pYthon')
print(m) # 매치 성공
□ MULTILINE
# MULTILINE
# 사용 X
p = re.compile('^python\s\w+')
text = '''python one
life is too short
python two
you need python
python three
'''
m = p.findall(text)
print(m) # ['python one']
# 사용 O
p = re.compile('^python\s\w+', re.MULTILINE)
m = p.findall(text)
print(m) # ['python one', 'python two', 'python three']
# ^를 쓰지 않고, MULTILINE 사용하지 않은 경우
p=re.compile('python\s\w+')
text = '''python one
life is too short
python two
you need python
python three
'''
m=p.findall(text)
print(m) #['python one', 'python two', 'python\npython']
■ 백슬래시
[가정] "\section" 문자열을 찾기 위한 정규식을 만들어야 하는 상황 | |
re.compile("\section") | 이 정규식은 \s문자가 공백으로 해석되어 의도한 대로 매치가 이뤄지지 않음. 왼쪽의 정규식은 [\t\n\r\f\v]ection 과 같은 의미로 해석됨. 결국 정규식을 사용한 \ 문자가 문자열 자체임을 알려주기 위해 \를 2개 사용하여 이스케이프를 처리해야 함. >>> re.compile("\\section") 하지만, 파이썬 정규식 엔진에는 파이썬 문자열 리터럴 규칙에 따라 \\ 이 \로 변경되어 결국 \section이 전달됨. 결국 파이썬 정규식 엔진에 \\문자를 전달하기 위해서는 백슬래시 4개 \\\\를 사용해야 함. |
이러한 문제로 인해 파이썬 정규식에는 Raw String 규칙이 생기게 됨. 따라서 자음과 같이 정규식 문자열 앞에 r문자를 삽입하면 Raw String 규칙에 의해 \ 1개만 써도 동일한 의미를 갖게 됨. >>> re.compile(r'\\section')
# 백슬래시 문제
import re
p= re.compile('\\section') #\section 으로 해석됨->[\t\r\n\v\f]ection 문자열과 일치한것을 찾음
m=p.search("What is \section and example?")
print(m) # None
m1=p.search("What is ection and example?")
print(m1) # <re.Match object; span=(7, 14), match=' ection'>
p =re.compile(r'\\section')
m=p.search("What is \section and example?")
print(m) # <re.Match object; span=(8, 16), match='\\section'>
■ 메타 문자
| | or 의미 |
^ | 문자열의 처음과 일치함 의미 |
$ | 문자열의 끝과 일치함 의미 |
\A | 문자열의 처음과 일치함 의미. ^은 MULTILINE 옵션을 사용할 경우 각 줄의 문자열의 처음과 매치되지만 \A는 줄과 상관 없이 전체 문자열의 처음하고만 일치됨. |
\Z | 문자열의 끝과 일치함을 의미. 여러 줄로 작성된 문자열에서 중과 상관 엇ㅂ이 전체 문자열의 끝과 일치됨. |
\b | 단어의 경계를 나타냄. 단어 경계는 단어 문자와 비단어 문자 사이의 위치를 의미함. 여기서 단어 문자는 보통 알파벳, 숫자, 밑줄 문자를 포함하며, 비단어 문자는 그 외의 문자임. \b는 백스페이스를 의미하므로 단어 구분자로 사용되기 위해서는 Raw String 임을 알려주는 r을 반드시 붙여줘야 함. |
\B | \b와 반대로 단어 앞뒤가 공백으로 구분된 단어가 아닌 경우에만 일치됨. |
□ |
# |
import re
p=re.compile('Crow|Servo')
m=p.match('ServoHello')
print(m) #<re.Match object; span=(0, 5), match='Servo'>
□ $
# $
print(re.search('short$','life is too short'))
print(re.search('short$','life is too short, you need python'))
#<re.Match object; span=(12, 17), match='short'>
#None
□ \A
# \A
# MULTILINE 옵션 안먹힘 !!!
# MULTILINE 옵션 적용하지 않은 ^와 동일한 의미
p = re.compile('\Apython\s\w+',re.MULTILINE)
text='''python one
life is too short
python two
you need python
python three'''
m=p.findall(text)
print(m)
□ \b
# \b
# 단어의 앞뒤가 공백으로 구분되어 있는지를 검사, 즉 단어가 독립적으로 있는지 확인
# \s는 공백을 포함하는지 검사
p=re.compile(r'\bclass\b')
print(p.search('no class at all'))
print(p.search('class at all'))
print(p.search('one subclass is'))
print(p.search('class '))
# 결과
# <re.Match object; span=(3, 8), match='class'>
# <re.Match object; span=(0, 5), match='class'>
# None
# <re.Match object; span=(0, 5), match='class'>
# \s의 경우
p=re.compile(r'\sclass\s')
print(p.search('no class at all'))
print(p.search('class at all'))
print(p.search('one subclass is'))
print(p.search('class '))
# 결과
# <re.Match object; span=(2, 9), match=' class '>
# None
# None
# None
□ \
# \
# 문자열 안에 포함된 메타문자(. ? $ 등)을 원재 문자 자체로 사용
m = re.search('안녕하세요?', '여러분 안녕하세요?')
print(m) #<re.Match object; span=(4, 9), match='안녕하세요'>
m = re.search('안녕하세요\?', '여러분 안녕하세요?')
print(m) #<re.Match object; span=(4, 10), match='안녕하세요?'>
■ 그룹핑
- () 메타문자는 그룹을 만듦
- 그룹을 만들면 group()함수를 사용하여 그룹핑된 부분의 문자열만 뽑아 낼 수 있음
- '\번호'를 이용하면 번호에 해당하는 그웁을 재참조함. 표현식은 Raw String으로 선언해야 함
- 그룹핑에 이름 붙이기: (?P<그룹이름>)
group(0) | 일치된 전체 문자열, group()와 동일 |
group(1) | 첫 번째 그룹에 해당하는 문자열 |
group(n) | n 번째 그룹에 해당하는 문자열 |
import re
p =re.compile('(ABC)+')
m =p.search('ABCABCABC OK?')
print(m)
print(m.group(0)) # or group(0) :패턴과 일치된 전체 문자열 반환
print(m.group(1)) # 첫 번째 그룹에 해당하는 문자열만 반환
#print(m.group(2)) # 에러 : group(n) n은 패턴에서 지정한 그룹의 수를 의미함.
# ABC가 3번 반복되었다고 해서 그룹이 3개가 생기는 것이 아님.
# 결과
# <re.Match object; span=(0, 9), match='ABCABCABC'>
# ABCABCABC
# ABC
# 이름과 전화번호를 그룹으로 지정하여 분리
s='park 010-1234-5678'
p=re.compile('(\w+)\s+(\d+-\d+-\d+)')
m=p.search(s)
print(m.group()) # park 010-1234-5678
print(m.group(1)) # park
print(m.group(2)) # 010-1234-5678
# 국번만 추출하고자 할 때
# 그룹이 중첩되어 있는 경우는 바깥쪽부터 시작하여 안쪽으로 들어갈 수록 인덱스 증가함
s='park 010-1234-5678'
s1='kim 02-356-7890'
p=re.compile('(\w+)\s+((\d{2,3})-\d{3,4}-\d{4})')
m=p.search(s)
m1=p.search(s1)
print(m.group(3)) # 010
print(m1.group(3)) # 02
# '\번호'를 이용한 그룹 재참조
# ** Raw String 이용해야 함
import re
re.match(r'(a)(b)\1\2','abab') # r'(a)(b)\1\2' == abab
# 동일한 단어가 연속적으로 사용된 문자열 찾기
# \b(\w+)\b : 단어 경계를 기준으로 문자와 숫자로 이루어진 한 단어를 찾음. 그리고 그 단어를 그룹으로 지정
# \s+ : 하나 이상의 공백문자를 지정
# \b\1\b : 단어 경계를 기준으로 한 단어를 찾으며, 그룹 참조를 통해서 이전에 그룹화된 단어와 동일한 단어를 찾음
p = re.compile(r'\b(\w+)\b\s+\b\1\b')
m =p.search('I have a dog dog in my house')
print(m.group()) #dog dog
print(m.group(1)) #dog
# 그룹에 이름 붙이기 (?p<그룹 이름>)
p = re.compile('(?P<name>\w+)\s+(?P<phone>\d+-\d+-\d+)')
m = p.search('park 010-1234-5678')
print(m.group())
print(m.group('name'))
print(m.group(1))
print(m.group('phone'))
print(m.group(2))
# 결과
# park 010-1234-5678
# park
# park
# 010-1234-5678
# 010-1234-5678
■ 전방 탐색
표현식1(?=표현식2) | 긍정 전방 탐색 | 표현식 1 뒤의 문자열이 표현식 2와 매치되면 표현식 1 일치. 표현식 2의 문자열은 결과로 반환하지 않음 |
표현식1(?! 표현식2) | 부정 전방 탐색 | 표현식 1 뒤의 문자열이 표현식 2와 매치되지 않으면 표현식 1 일치. 표현식 2의 문자열은 결과로 반환하지 않음. |
# 긍정 전방 탐색
import re
# url에서 프로토콜의 이름만 가져오기
p = re.compile('.+(?=:)')
print(m.group()) # http
# 부정 전방 탐색
# 파일 이름의 확장자 중 bat파일만 제외하고 추출
file_name = ['autoexe.bat','python.exe','sysinfo.cf']
p =re.compile('[a-zA-Z]+\w*[.](?!bat)') # 확장자 앞의 파일의 이름만 가져와서 리스트에 존재하는 파일명 가져옴
for f in file_name:
if p.search(f):
print(f)
# 결과
# python.exe
# sysinfo.cf
print()
p1= re.compile('[a-zA-Z]+\w*[.](?!bat)[a-zA-Z]+')
for f in file_name:
if p1.search(f):
print(f)
# 결과
# python.exe
# sysinfo.cf
■ 후방 탐색
: 텍스트를 반환하기 전에 뒤쪽을 탐색하는 것
(?<=표현식2) 표현식1 | 긍정 후방 탐색 | 표현식1 앞의 문자열이 표현식 2와 매치되면 표현식1 일치. 표현식2의 문자열은 결과로 반환하지 않음 |
(?<!표현식2) 표현식1 | 부정 후방 탐색 | 표현식 1 앞의 문자열이 표현식 2와 매치되지 않으면 표현식1 일치. 표현식 2의 문자열은 결과로 반환하지 않음 |
# 긍정 후방 탐색
p=re.compile('(?<=\$)\d+[.]\d+') # 통화기호가 $인 경우 금액(소수점 존재하는)만 뽑는 패턴
p1=re.compile('(?<=\$)[0-9]+[.]?[0-9]*') # 소수점이하가 없는 경우도 적용가능한 패턴
m=p.search('ABC01: $23.1')
m1=p1.search('ABC01: $23')
print(m.group()) #23.1
print(m1.group()) #23
■ 전후방 동시 탐색
# 전후방 동시 탐색
# <p>~ </p> 태그 사이의 문자열 추출하기
# 뒤에서 탐색했을 시 <p>까지 일치가 되어야 하고, 앞에서 탐색했을 시에는 </p>까지 일치되어야 함
p=re.compile('(?<=<p>)\w+(?=</p>)')
print(p.search('kakao <p>ryan</p> keep a straight face').group()) #ryan
print(p.search('<p>HelloRyan</p>').group()) #HelloRyan
■ 문자열 바꾸기
# pattern객체.sub(바꿀 문자열, 대상 문자열, 바꿀 횟수)
p= re.compile('blue|white|red')
p.sub('color','blue socks and red shoes',count=0) # count=0 : 매치되는 문자열 전체 다 바꿈
# > 'color socks and color shoes'
p.sub('color','blue socks and red shoes',count=1)
# > 'color socks and red shoes'
p.sub('color','blue socks and red shoes',count=2)
# > 'color socks and color shoes'
[ 문제 1 ] 이메일 형식 검증
: 사용자로부터 입력받은 임의의 이메일 주소에 대해 유효한 형식인지를 검증하는 함수 작성
def valid_email(email):
return '이메일 검증 결과'
일반적인 이메일 형식 : 알파벳 및 숫자 @도메인이름
위 함수는 다음의 이메일을 올바른 이메일 형식으로 결과 값을 반환한다.
- mimke@korea.co.kr
- mike@daum.net
- mike.kim@gmail.com
위 함수는 다음의 이메일을 올바른 이메일 형식이 아님을 결과 값으로 반환한다
- mysite.com
- mike@good
import re
def valid_email(email):
p = re.compile('[a-zA-Z]\w*[_]*\w*[@]\w+[.]\w+([.]\w+)*')
#p = re.compile('[a-zA-Z]+\w*[.]?\w*[@]\w+[.]\w+[.]?\w{2,3}')
if p.match(email):
return True
else:
return False
email = input('이메일 입력 : ')
if valid_email(email):
print('유효한 이메일 형식입니다.')
else:
print('유효하지 않은 이메일입니다.')
[ 문제 2 ] 한글 찾기, 한글 제거
s = 'i am 신뢰에요. 알겠죠? 그런 식이면 Just 멈춰줘요'
m1 = re.findall('[ㄱ-힡]+',s) # 한글만 찾기
m = re.findall('[a-zA-Z]+',s) # 한글만 제거
print(m1) # ['신뢰에요', '알겠죠', '그런', '식이면', '멈춰줘요']
print(m) # ['i', 'am', 'Just']
[ 문제 3 ] 주민번호의 유효성 검증
: 사용자로부터 입력 받은 임의의 주민등록번호가 올바른 형식인지 검증하는 정규표현식 작성
: 주민번호 형식 : 생년월일(6자리)-성별(1자리)나머지 6자리 숫자
- 년도 : 숫자 두 자리
- 월 : 앞자리가 0일 때 뒷 자리는 1~9까지 허용, 1일 때는 0~2까지만 허용
- 일 : 앞자리가 0일 때 뒷 자리는 1~9까지, 앞자리가 1또는 2일 때 뒷자리는 0~9까지 허용,
앞자리 3일 때는 0과 1만 허용
- 성별 숫자 : 1~4
- 나머지 6자리 : 숫자인지만 체크
# 1)
import re
def valid_id (id):
pattern = re.compile('[0-9]{2}(0[0][1-9]|1[0-2])(0[1-9]|(1|2)\d|3[0-1])-[1-4]\d{6}')
if pattern.match(id):
return True
else:
return False
id = input('주민번호 입력 : ')
if valid_id(id):
print(f'{id[:7]}-{"*"*7}은 유효한 주민번호 형식입니다.')
else:
print(f'{id[:7]}-{"*"*7}은 유효한 주민번호 형식이 아닙니다.')
# 2) 문자열 대체 방식 사용
import re
def valid_id (id):
pattern = re.compile('([0-9]{2}(0[1-9]|1[0-2])(0[1-9]|(1|2)\d|3[0-1]))-[1-4]\d{6}')
if pattern.match(id):
print(pattern.sub(r'\1-*******',id),'은 유효한 주민 번호 형식입니다.',sep='')
else:
print(pattern.sub(r'\1-*******',id),'은 유효한 주민 번호 형식이 아닙니다.',sep='')
id = input('주민번호 입력 : ')
valid_id(id)
Reference : https://sesoc.tistory.com/252
'Data Analysis' 카테고리의 다른 글
BeautifulSoup 모듈 (0) | 2023.11.07 |
---|---|
Selenium 모듈 (0) | 2023.11.06 |
정규 표현식 part2 (1) | 2023.11.01 |
정규 표현식 part1 (0) | 2023.11.01 |
[Web Scrapping] requests 모듈 (0) | 2023.11.01 |