1. 정규표현식에서 a.b와 a[.]b 의 차이점을 서술하시오
[답변]
. : a와 b 사이에 문자가 있음
[.] : 문자 그대로의 dot
2. 정규식 a[.]b는 "a0b" 문자열과는 매ㅣ되고
"a.b"와는 매치되지 않는다 (ox)
[답변]o
3. 다음의 문자열 중에서
정규식 cb*s 와 매칭되는 문자열의 갯수를 모두 고르시오
[문자열]
"cs", "cbbbs", "bbscc", "cbbbbs", "bbbs", "cbs"
[답변] 4개
*는 0~약 2억개 까지의 반복
4. 문자와 숫자 아닌 문자와 매치되는
[^a-zA-z0-9]와 동일한 표현식은?
[답변] \W
5.숫자와 매치되는 [0-9]와 동일한 표현식은?
[답변] \d
6. random 모듈을 사용하여 [1, 2, 3, 4, 5]를
무작위로 섞어서 출력하시오
[답변]
import random
a = [1, 2, 3, 4, 5]
random.shuffle(a)
7. map과 lambda를 사용하여 [5, 6, 7, 8] 리스트의
각 요소값에 5가 곱해진 리스트 b를 만들어보자
[답변]
a = [5, 6, 7, 8]
b = list(map(lambda x:x*5, a))
8. while 문으로 1-9까지 반복하는 반복문
[답변]
i = 1
while i < 10 :
print(i)
i += 1
9. a=[1, 2, 3, 4,] 리스트를 for문으로
li 리스트 안에 [5, 10, 15, 20] 넣기
[답변]
a = [1, 2, 3, 4, 5]
li = []
for i in a :
li.append(i * 5)
10. 정규식을 사용하여 "I like apble And abple" 문장을 수정하세요
[답변]
import re
string = "I like apble And abple"
re.sub("apble|abple", "apple", string)
11. 메타문자에서 $는 무슨 뜻인가?
[답변] 마지막, 끝
12. 정규식에 쓰이는 반복표현을 나열하시오
[답변] +, *, {m, n}, ?
13. 아래 전화번호 리스트를 010-1**-**** 형태로 출력하시오
[답변]
phone_number= ['010-123-1234', '010-111-1111',
'010-222-2222', '010-333-3333',
'010-444-4444', '010-555-5555',
'010-666-6666', '010-777-7777',
'010-888-8888', '010-999-9999']
for Number in phone_number :
# print(re.sub("[0-9][0-9]-[0-9]{4}", "**-****", Number))
# print(re.sub("\d{2}-\d{4}", "**-****", Number))
print(re.sub(Number[5:], "**-****", Number))
14. 외자인 이름을 'A'로 바꿔주세요
li = ['손흥민', '황의조', '이 호', '황희찬', '홍 철']
[답변]
li = ['손흥민', '황의조', '이 호', '황희찬', '홍 철']
for name in li :
# print(re.sub("\w[ ]\w", "A", name))
# print(re.sub(".[ ].", "A", name))
# print(re.sub("\w\s\w", "A", name))
# print(re.sub("\D \D", "A", name))
print(re.sub("\w \w", "A", name))
15. 20개의 문자열을 가진 리스트를 만드세요
조건 1. 각 문자열의 길이는 최대 10, 최소 1
조건 2. 각각 33.3333 퍼센트의 확률로 문자열(string) 안의 문자(char)
는 대문자 이거나 소문자 이거나 숫자
[답변]
words = []
for count in range(20) :
word = ""
while len(word) < random.randint(1, 10) :
word += ''.join(random.choice(str(random.randint(0,9)) + chr(random.randint(65, 90))+chr(random.randint(97, 122))))
words.append(word)
print(words)
[추가답변]
words = []
for count in range(20):
word = ''
while len(word) < random.randint(1,10):
a = random.randint(1,3)
if a == 1:
English = chr(random.randint(65,90))
word += English
elif a == 2:
english = chr(random.randint(97,122))
word += english
else:
number = str(random.randint(0,9))
word += number
words.append(word)
print(words)
15-1. 리스트 words 안의 문자열 중
시작은 소문자, 끝은 대문자인 문자열을 #으로 대체하세요.
[답변]
words_2 = []
for string in words :
b = re.sub("^[a-z].+[A-Z]$", "#", string)
words_2.append(b)
words_2
match()객체를 사용하다보면 궁금할 수 있다. 어떤 문자열이 매치되었는가. 이때 사용할 수 있는 메서드가 있다
| 메서드 | 목적 |
|---|---|
| group() | 매치된 문자열을 돌려준다 |
| start() | 매치된 문자열의 시작 위치를 돌려준다 |
| end() | 매치된 문자열의 끝 위치를 돌려준다 |
| span() | 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다. |
import re
# '모든 영어 문자들'을 컴파일 해주기
p = re.compile('[a-z]+')
m = p.match("python")
# 매치하기 위해 넣어준 문자열 확인
m.group()
> 'python'
# 어느 인덱스부터 시작했는가
# match는 가장 처음부터 매칭한다
m.start()
> 0
# 만약 search로 하면
s = p.search("3 python")
s.start()
> 2
# 매칭문자열의 끝 인덱스
m.end()
> 6
# 매치된 문자열의 시작끝 인덱스가 있는 튜플
m.span()
> (0, 6)
1) 전화번호 골라내기
# 전화번호 골라내기
data = ["010-1234-5678", "010-1234-5679",
"010-1234-5670", "010-1234-670",
"02-334-2243"]
# data 리스트에서 올바른 전화번호만 골라내세요
data = ["010-1234-5678", "010-1234-5679",
"010-1234-5670", "010-1234-670",
"02-334-2243"]
def is_phone_number_ok(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_ok(data)
# 굳이 group을 사용한다면...
is_phone_number_ok = []
p = re.compile("010-\d{4}-\d{4}")
for i in data :
a = p.search(i)
if a :
is_phone_number_ok.append(a.group())
is_phone_number_ok
# finditer를 사용한다면
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)
# findall 사용한다면
re.findall('010-\d{4}-\d{4}', data)
2) 문자열에서 span()
# 하세요, 하지요, 싶어요 의 span()을 출력하세요
msg = """안녕하세요. 제 소개부터 드리도록 하지요.
저는 의미있는 일을 하고 싶어요. 건강하세요."""
for i in re.finditer('하세요|하지요|싶어요',msg):
print(i.span())
메타문자 dot(.)은 \n을 제외한 모든 문자와 매치된다는 규칙이 있다. 그런데 만약 \n도 포함하여 매치하고 싶다면 DOTALL 혹은 S 옵션을 사용하여 컴파일 하면 된다.
import re
# a와 b사이에 모든 문자
p = re.compile('a.b')
m = p.match('a\nb')
m
> 아무것도 나오지 않는다
# compile 시킬때 넣는 옵션
p = re.compile('a.b', re.DOTALL)
m = p.match('a\nb')
m
> <re.Match object; span=(0, 3), match='a\nb'>
# re.S도 동일하게 작동
p = re.compile('a.b', re.S)
원래 [a-z] 는 소문자, [A-Z] 는 대문자만을 의미하지만 이 옵션으로는 대소문자 구별없이 매치된다.
import re
p = re.compile('[a-z]', re.I)
m = p.match('python')
n = p.match('PYTHON')
print(m, n)
> <re.Match object; span=(0, 1), match='p'> <re.Match object; span=(0, 1), match='P'>
p = re.compile('[A-Z]', re.IGNORECASE)
m = p.match('python')
n = p.match('PYTHON')
print(m, n)
> <re.Match object; span=(0, 1), match='p'> <re.Match object; span=(0, 1), match='P'>
정규 표현식을 파이선에서 사용할 때 혼란을 주는 요소가 있다면 \n 일 것이다.
\find 은 [\t\n\s]end 와 같이 \s가 공백으로 인식되는 문제가 발생될 수 있다. 이럴때는 사용한 \가 문자 자체임을 알려주기 위해 백슬래시 2개를 사용하여 이스케이프 처리를 해야한다.
\find 가 문자 자체라면
re.compile('\find') --- x
re.compile('\\find') -- o
그런데 만약.. \가 한번이 아니라 \\\\\와 같이 여러번 쓰인다면 이를 표현하기위해 \\\\\\\\\\ 를 써줘야 하는 일이 발생한다. 이런 비효율적인..
이러한 문제로 인해 파이썬 정규식에는 Raw String 규칙이 생겨났다.
p = re.compile(r'\\\\\find')
위와 같이 정규식 문자열 앞에 r 을 삽입하면 이 식은 백슬래시를 여러번 사용할 필요없이 그 자체로 문자인식이 가능하다.
1) \b
단어 구분자. 공백이라고 생각하면 된다.
p = re.compile(r"\bclcass\b")
p.search('no class at all')
> 매치
p.search('no bclass at all')
> 매치 x
> 문자열 안에 class라는 단어가 포함되어 있기는 하지만
공백으로 구분된 단어가 아니므로
2) \B
공백이 아닌 것.
p.re.compile(r'\Bclass\B')
p.search('no class at all')
> 매치 x
p.search('no bclassa at all')
> 매치 o
보통 반복되는 문자열을 찾을 때 그룹을 사용하는데, 그룹을 사용하는 보다 큰 이유는 매치된 문자열 중에서 특정 부분의 문자열만 뽑아내기 위해서인 경우가 더 많다.
패턴 안에서 정규표현식을 괄호()로 묶으면 그룹이된다
sentence = "park 010-1234-5678"
p = re.compile(r'(\w+)\s(\d+)[-]\d+[-]\d+')
p.search(sentence).group(0)
> 'park 010-1234-5678'
group 메서드의 인덱스는 다음과 같은 의미를 가진다.
| group(인덱스) | 설명 |
|---|---|
| group(0) | 매치된 전체 문자열 |
| group(1) | 첫 번째 그룹에 해당하는 문자열 |
| group(2) | 두 번째 그룹에 해당하는 문자열 |
| group(3) | n번째 그룹에 해당하는 문자열 |
(?P<그룹 이름>) 의 형태
그룹의 개수가 많아지면 구분하기가 힘들어 진다. 이때 그룹에 이름을 지어 관리하면 편하다는데, 반복적인 정규식을 사용할 때 좋은 것 같다.
`(?P<그룹 이름>\w) = (\w+)`
# 이름을 지정하고 이름으로 참조하기
p = re.compile(r'(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)')
m = p.search("park 010-1234-5678")
print(m.group("name"))
# 이름으로 정규식 안에서 재참조
# 같은 두 단어가 반복되는 거 찾기
p = re.compile(r'(?P<word>\w+)\s+(?P=word)')
p.search('Paris in the spring spring')
p = re.compile(".+:")
m = p.search("http://google.com")
m.group()
> http:
정규식 .+ 와 일치하는 문자열로 http: 가 결과로 나왔다. 이 결과에서 :을 제외하고 그루핑은 추가로 할 수 없다는 조건까지 더해진다면 어떡할까?
| 정규식 | 종류 | 설명 |
|---|---|---|
| (?= ) | 긍정형 전방 탐색 | 해당하는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소비되지 않는다 |
| (?!= ) | 부정형 전방 탐색 | 해당하는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소비되지 않는다 |
검색에서는 기존 정규식과 동일한 효과를 발휘하지만 :에 해당하는 문자열이 정규식 엔진에 의해 소비되지 않아 (검색에는 포함되지만 검색결과에서는 제외) 검색 결과에서는 제거된 것 처럼 출력되는 효과가 있다.
→ 그 앞까지만 찾아주는 것처럼 보이는 듯
p = re.compile(".+(?=:)")
m = p.search("http://google.com")
m.group()
> 'http'
p = re.compile(".+(?=0)")
m = p.search("hello2022")
m.group()
> 'hello2'
-가 아닌 경우에만 통과된다는 의미로, 해당 문자열이 있는지 조사하는 과정에서 문자열이 소비되지 않으므로 (걸러지기때문에) 아니라고 판단 되어야 그 이후 정규식 매치가 진행된다.
# 예를 들어 확장자가 bat이 아닌것만 걸러내고 싶다
.*[.](?!bat$).*$
# exe인것도 걸러내고 싶다
.*[.](?!bat$|exd$).*$
(?<= ) 뒤부터 해당 문자열까지만 탐색
p = re.compile("(?<=o).+")
m = p.search("hello2022")
m.group()
> '2022'
data = '''
http://www.forta.com a
https://mail.forta.com b
ftp://ftp.forta.com c
'''
p = re.compile("(?<=f).+")
p.findall(data)
# > ['orta.com a', 'orta.com b', 'tp://ftp.forta.com c']
p = re.compile(r"f\w+[.]\w{3}")
p.findall(data)
# > ['forta.com', 'forta.com', 'ftp.for']
p = re.compile(r"[.](?=f).+")
p.findall(data)
# > ['.forta.com a', '.forta.com b', '.forta.com c']
p = re.compile("(?<=[.]).+")
p.findall(data)
# > ['forta.com a', 'forta.com b', 'forta.com c']
정규표현식은 기본적으로 탐욕적(Greedy) 방식으로 동작한다. 의도치않게 너무 많이 일치하는 경우가 발생하는 이유 중 하나이다.
s = 'lgreedylazyUseregularexpressionsa'
# 위 문자열에서 l로 시작하고 a로 끝나는 문자를 찾는다면
p = re.compile("l.+a")
p.search(s).group()
# > 'lgreedylazyUseregularexpressionsa'
결과로 문자열 전체가 일치하게 되었습니다. 문자열에서 l로 시작하고 a로 끝나는 모든 문자를 찾고싶다면 Lazy 방식으로 검색해야하는데
? 문자를 해당 메타문자 뒤에 붙이면 됩니다. *? 처럼 말이죠
# 문자열
lgreedylazyUseregularexpressionsa
# 정규표현식
l.+?a