-re.compile을 사용하여 정규표현식을 컴파일 한다
-re.compile("패턴") : 괄호 안의 패턴(=조건)과 매치되는지 조사해라, 매치되는 것을 찾아라
-객체 p : re.compile 한 결과
>>> p = re.compile("^[a-z].+[A-Z]$")
type(p)
re.Pattern
>>> for word in words:
p = re.compile("^[a-z].+[A-Z]$")
p.sub("#", word)
print(words)
#for문 안의 코드 두줄 : re.sub("^[a-z].+[A-Z]$", "#", word) 와 동일한 표현
#complie로 코딩하는 것을 더 권장 -> 메모리를 절약할 수 있고, 속도가 더 빠르다
>>> p = re.compile("[a-z]+")
>>> p.match("python")
<re.Match object; span=(0, 6), match='python'>
#"python" 문자열은 컴파일 된 정규식 "[a-z]+" 에 부합되므로 match 객체를 돌려준다
#span=(0, 6) : 매치된 문자열의 시작과 끝을 알려준다 (인덱스 번호)
>>> re.match("[a-z]+", "python")
<re.Match object; span=(0, 6), match='python'>
>>> p.match("pythonPYTHON1234")
<re.Match object; span=(0, 6), match='python'>
>>> p.match("PYTHONpython1234")
#아무런 결과가 출력되지 않는다
#match는 문자열의 '처음부터' 매치되는지를 조사한다
>>> p.search("PYTHONpython1234")
<re.Match object; span=(6, 12), match='python'>
#search는 문자열 전체를 조사하기 때문에 매치되는 부분을 확인할 수 있다
>>> p.search("PYTHONpython1234 pythonPYTHON1234")
<re.Match object; span=(6, 12), match='python'>
문자열의 처음부터 검색할지의 여부에 따라 다르게 사용해야한다
정규식과 매치될 때는 match 객체를 돌려주고, 매치되지 않을 때는 None을 돌려준다
문자열 안에 띄어쓰기로 구분되어있는 단어를 각각 정규식과 매치해서 리스트로 돌려준다
>>> p.findall("PYTHONpython1234 pythonPYTHON1234")
['python', 'python']
-주로 for 문이 함께 사용된다
-findall() 과 동일하지만 그 결과로 반복 가능한 객체(iterator object)를 돌려준다
-반복 가능한 객체가 포함하는 각각의 요소는 match 객체이다
>>> p.finditer("PYTHONpython1234 pythonPYTHON1234")
<callable_iterator at 0x1aaab276020>
>>> for i in p.finditer("PYTHONpython1234 pythonPYTHON1234"):
print(i)
<re.Match object; span=(6, 12), match='phthon'>
<re.Match object; span=(19, 25), match='phthon'>
>>> p = re.compile("[a-z]+")
>>> p.match("pythonPYTHON1234 PYTHONpython1234")
<re.Match object; span=(0, 6), match='python'>
#group() : 매치된 문자열을 찾아서 보여준다
>>> p.match("pythonPYTHON1234 PYTHONpython1234").group()
'python'
#span() : 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다
>>> p.match("pythonPYTHON1234 PYTHONpython1234").span()
(0, 6)
#start() : 매치된 문자열의 시작 위치를 알려준다
>>> p.match("pythonPYTHON1234 PYTHONpython1234").start()
0
#match 메서드를 수행한 결과로 돌려준 match 객체의 start() 결괏값은 항상 0 이다
#match 메서드는 항상 문자열의 시작부터 조사하기 때문이다
#search 메서드를 사용한다면 start() 값은 다르게 나올 것이다
#end() : 매치된 문자열의 끝 위치를 알려준다
>>> p.match("pythonPYTHON1234 PYTHONpython1234").end()
6
>>> p = re.compile("[a-z]+")
>>> p.search("PYTHONpython1234").group()
>>> p.search("PYTHONpython1234").span()
>>> p.search("PYTHONpython1234").start()
>>> p.search("PYTHONpython1234").end()
>>> data = ["010-1234-5678", "010-1234-5679", "010-1234-5670", "010-1234-670", "02-334-2243"]
#핸드폰 전화번호 골라내기
>>> for i in data:
print(re.search("010-\d{4}-\d{4}", i))
<re.Match object; span=(0, 13), match='010-1234-5678'>
<re.Match object; span=(0, 13), match='010-1234-5679'>
<re.Match object; span=(0, 13), match='010-1234-5670'>
None
None
>>> data = ["010-1234-5678", "010-1234-5679", "010-1234-5670", "010-1234-670", "02-334-2243"]
def is_phone_number(data):
phone_number = []
for i in data:
if re.search("010-\d{4}-\d{4}", i):
phone_number.append(i)
return phone_number
is_phone_number(data)
['010-1234-5678', '010-1234-5679', '010-1234-5670']
#finditer , group 을 사용하여
>>> data = "010-1234-5678, 010-1234-5679, 010-1234-5670, 010-1234-670, 02-334-2243"
def is_phone_number(data):
phone_number = []
for i in re.finditer("010-\d{4}-\d{4}", data):
phone_number.append(i.group())
return phone_number
is_phone_number(data)
['010-1234-5678', '010-1234-5679', '010-1234-5670']
#findall 을 사용하여
>>> data = "010-1234-5678, 010-1234-5679, 010-1234-5670, 010-1234-670, 02-334-2243"
re.findall("010-\d{4}-\d{4}", data)
['010-1234-5678', '010-1234-5679', '010-1234-5670']
#'하세요', '하지요', '싶어요' 의 span()을 출력
>>> msg = "안녕하세요. 제 소개부터 드리도록 하지요. 저는 의미있는 일을 하고 싶어요. 건강하세요."
p = re.compile("하세요|하지요|싶어요")
for m in p.finditer(msg):
print(m.span())
(2, 5)
(19, 22)
(38, 41)
(45, 48)
>>> for m in re.finditer("하세요|하지요|싶어요", msg):
print(m.span())
#이름 + " " + 전화번호 형태의 문자열
>>> sentence = "park 010-1234-1234"
p = re.compile(r'(\w+)\s((\d+)[-]\d+[-]\d+)')
>>> p.search(sentence)
<re.Match object; span=(0, 18), match='park 010-1234-1234'>
\b 메타 문자의 경우 -> \b는 파이썬 리터럴 규칙에 의하면 백스페이스(Backspace)를 의미하므로, 백스페이스가 아닌 단어 구분자 역할을 하는 메타 문자임을 알려주기 위해r'\bclass\b' 처럼 앞에 기호 r 을 반드시 붙여준다group(0) : 매치된 전체 문자열
group(n) : n번째 그룹에 해당하는 문자열
>>> p.search(sentence).group(0)
'park 010-1234-1234'
>>> print(p.search(sentence).group(2), p.search(sentence).group(1))
010-1234-1234 park
>>> print(p.search(sentence).group(3))
010
정규식 안에 그룹이 많아지는 경우, 정규식이 수정되면서 그룹이 추가 또는 삭제되면 그 그룹을 인덱스로 참조한 프로그램도 모두 변경해 주어야하는 위험이 생길 수 있다
-> 그룹에 '이름(Name Groups)'을 지정해줄 수 있다
>>> sentence = "park 010-1234-1234"
p = re.compile(r'(\w+)\s((\d+)[-]\d+[-]\d+)')
#'이름(group(1))'에 'family', '010(group(3))'에 'phone_first'
#(\w+) -> (?P<family>\w+), (\d+) -> (?P<phone_first>\d+)
>>> sentence = "park 010-1234-1234"
p = re.compile(r'(?P<family>\w+)\s((?P<phone_first>\d+)[-]\d+[-]\d+)')
>>> p.search(sentence).group("family")
'park'
>>> p.search(sentence).group("phone_first")
010
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
p.search('Paris in the the spring').group()
'the the'
>>> p = re.compile(".+:") #':' 앞의 모든 문자
m = p.search("http://google.com")
m
<re.Match object; span=(0, 5), match='http:'>
>>> m.group()
'http:'
#정규식 ".+:" 과 일치하는 문자열로 'http:' 를 돌려주었다 -> ':'를 포함하고 있다
#1. 긍정형 전방 탐색 : (?=...)
#'...' 에 해당하는 정규식과 매치되어야 하며, 조건이 통과되어도 검색 결과에서는 제외된다
>>> p = re.compile(".+(?=:)")
m = p.search("http://google.com")
m.group()
'http'
#2. 부정형 전방 탐색 : (?!...)
#'...' 에 해당하는 정규식과 매치되지 않아야 하며, 조건이 통과되어도 검색 결과에서는 제외된다
>>> sent = "aaa.exe\nbbb.pdf\nccc.hwp"
p = re.compile(".*[.](?!exe$).*$")
c = p.search(sent)
c.group()
'ccc.hwp'
>>> p = re.compile("(?<=:).+")
m = p.search("http://google.com")
m.group()
'//google.com' #':'이 제외되고 출력되었다
>>> p = re.compile("(?<=ll).+")
m = p.search("hello2022")
m.group()
'o2022'
>>> p = re.compile("(?=ll).+")
m = p.search("hello2022")
m.group()
'llo2022'
>>> p = re.compile(".+(?=ll)")
m = p.search("hello2022")
m.group()
'he'
>>> p = re.compile(".+(?<=ll)")
m = p.search("hello2022")
m.group()
'hell'
후방 탐색에서 '<' 를 빼면 '...'에 해당되는 부분이 함께 출력이 되고,
전방 탐색에서 '<' 를 붙이면 '...'에 해당되는 부분이 함께 출력이 된다
#'log:' 를 기준으로 전방 탐색, 후방 탐색
>>> text = '''
2018-05-12 00:00:01 ABC DEFG log: this is python
2018-05-12 00:00:02 ABC DEFG HI log: this is python
2018-05-12 00:00:03 ABC DEFG HI JKL log: this is python
'''
#전방 탐색
>>> p = re.compile('.+(?=log:)')
for i in p.finditer(text):
print(i.group())
2018-05-12 00:00:01 ABC DEFG
2018-05-12 00:00:02 ABC DEFG HI
2018-05-12 00:00:03 ABC DEFG HI JKL
>>> p = re.compile('.+(?=log:)')
p.findall(text)
['2018-05-12 00:00:01 ABC DEFG ',
'2018-05-12 00:00:02 ABC DEFG HI ',
'2018-05-12 00:00:03 ABC DEFG HI JKL ']
#후방 탐색
>>> p = re.compile('(?<=log:).+')
for i in p.finditer(text):
print(i.group())
this is python
this is python
this is python
>>> p = re.compile('(?<=log:).+')
p.findall(text)
[' this is python', ' this is python', ' this is python']
>>> p = re.compile(r"(?P<name>\w+)\s+(?P<phone>(\d+)[-]\d+[-]\d+)")
print(p.sub("\g<2> \g<1>", "park 010-1234-1234"))
010-1234-1234 park
>>> data = '''
http://www.forta.com
https://mail.forta.com
'''
>>> p = re.compile(r"f\w+[.]\w{3}")
p.findall(data)
['forta.com', 'forta.com']
>>> p = re.compile(r".+f")
p.findall(data)
['http://www.f', 'https://mail.f']
>>> p = re.compile(r".+(?=f)")
p.findall(data)
['http://www.', 'https://mail.']
>>> p = re.compile(r"(?=f).+")
p.findall(data)
['forta.com', 'forta.com']
>>> p = re.compile(r"(?<=f).+")
p.findall(data)
['orta.com', 'orta.com']
greedy : 탐욕스러운
#Greedy : 최대한의 문자열을 소비한다
>>> data = ["soso", "sososo", "sosososo", "sososososo"]
p = re.compile(r'(so){3,5}') #'so'가 3번 이상 5번 이하 반복
for i in data:
print(p.search(i))
None
<re.Match object; span=(0, 6), match='sososo'>
<re.Match object; span=(0, 8), match='sosososo'>
<re.Match object; span=(0, 10), match='sososososo'>
#Non-Greedy : '?' 문자를 사용하여 제한한다
>>> data = ["soso", "sososo", "sosososo", "sososososo"]
p = re.compile(r'(so){3,5}?')
for i in data:
print(p.search(i))
None
<re.Match object; span=(0, 6), match='sososo'>
<re.Match object; span=(0, 6), match='sososo'>
<re.Match object; span=(0, 6), match='sososo'>