Ver3.5 ๐ ์ ๊ทํํ์๊ณผ ํ ์คํธ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ณ ๊ธ ๋ฌธ์์ด ์กฐ์ ์์ ์ ๋ณต

๐ ์ ๊ทํํ์๊ณผ ํ ์คํธ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ณ ๊ธ ๋ฌธ์์ด ์กฐ์ ์์ ์ ๋ณต
Python์ผ๋ก ํ ์คํธ๋ฅผ ๋ง๋ฒ์ฒ๋ผ ๋ค๋ฃจ๋ ์ค์ ๊ฐ์ด๋ โ ํจํด ๋งค์นญ๋ถํฐ ๊ณ ๊ธ ์กฐ์๊น์ง
์ผ, ์์งํ ๋งํด๋ด. ๐
์ฝ๋ฉํ๋ค๊ฐ ํ
์คํธ ์ฒ๋ฆฌํ ๋ "์ด๊ฑฐ ์ด๋ป๊ฒ ๋ฝ์๋ด์ง?" ํ๋ฉด์ ๋ฉ ๋๋ฆฐ ์ ์์ง ์์?
๋ก๊ทธ ํ์ผ์์ ํน์ ํจํด ์ฐพ๊ธฐ, ์ด๋ฉ์ผ ์ฃผ์ ์ ํจ์ฑ ๊ฒ์ฌ, HTML ํ๊ทธ ์ ๊ฑฐ, ์ ํ๋ฒํธ ํ์ ํต์ผโฆ
์ด๋ฐ ์์
๋ค์ ์ผ์ผ์ด ๋ฐ๋ณต๋ฌธ์ผ๋ก ์ฒ๋ฆฌํ๋ค ๋ณด๋ฉด ์ฝ๋๊ฐ ์คํ๊ฒํฐ๊ฐ ๋๋ ๊ฑด ์๊ฐ๋ฌธ์ ์ผ.
๊ทธ๋์ ์ค๋์ Python์ ์ ๊ทํํ์(Regular Expression)๊ณผ ๊ณ ๊ธ ๋ฌธ์์ด ์กฐ์ ๊ธฐ๋ฒ์ ์์ ํ ํํค์ณ๋ณผ ๊ฑฐ์ผ.
์ฒ์์ ์ข ๋ฏ์ค ์ ์๋๋ฐ, ํ ๋ฒ ์ตํ๋ฉด "์ด๊ฑธ ์ ์ด์ ์ผ ์์์ง?" ํ๋ ์๊ฐ์ด ๋ฐ๋์ ์จ๋ค๊ณ . ๐ฅ
์, ์ค๋น๋์ด? ๊ฐ์ด ๋ฌ๋ ค๋ณด์!
๐ 1. ์ ๊ทํํ์์ด ๋ญ๋ฐ? โ ๊ธฐ์ด ๊ฐ๋ ์์ ์ ๋ฆฌ
์ ๊ทํํ์(Regular Expression, ์ค์ฌ์ regex ๋๋ regexp)์ ๋ฌธ์์ด์์ ํน์ ํจํด์ ์ฐพ๊ฑฐ๋, ๋์ฒดํ๊ฑฐ๋, ์ถ์ถํ๊ธฐ ์ํ ํน์ํ ๋ฌธ์ ์ํ์ค์ผ.
1950๋
๋ ์ํ์ ์คํฐ๋ธ ํด๋ ์ด๋(Stephen Kleene)๊ฐ ์ ๊ท ์ธ์ด๋ฅผ ํํํ๊ธฐ ์ํด ๊ฐ๋ฐํ ๊ฐ๋
์ธ๋ฐ, ์ง๊ธ์ ๊ฑฐ์ ๋ชจ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์ง์ํด.
Python์์๋ re ๋ชจ๋์ ํตํด ์ ๊ทํํ์์ ์ฌ์ฉํ ์ ์์ด. ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํฌํจ๋์ด ์์ผ๋๊น ๋ณ๋ ์ค์น ์์ด ๋ฐ๋ก ์ธ ์ ์์ง!
๐ง re ๋ชจ๋ ์ํฌํธ์ ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
Python์์ ์ ๊ทํํ์์ ์ฐ๋ ค๋ฉด ๋จผ์ re ๋ชจ๋์ ์ํฌํธํด์ผ ํด. ์์ฃผ ๊ฐ๋จํด!
import re
# ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ
text = "Python์ ์ ๋ง ์ฌ๋ฏธ์๋ ์ธ์ด์ผ! 2024๋
์๋ ์ต๊ณ !"
# ์ซ์ ์ฐพ๊ธฐ
result = re.search(r'\d+', text)
if result:
print(result.group()) # ์ถ๋ ฅ: 2024
# ๋ชจ๋ ์ซ์ ์ฐพ๊ธฐ
all_numbers = re.findall(r'\d+', text)
print(all_numbers) # ์ถ๋ ฅ: ['2024']
๐ฏ 2. re ๋ชจ๋์ ํต์ฌ ํจ์๋ค โ ์๋ฒฝ ํด๋ถ
re ๋ชจ๋์๋ ๋ค์ํ ํจ์๋ค์ด ์๋๋ฐ, ๊ฐ๊ฐ ์ฐ์์๊ฐ ๋ฌ๋ผ.
์ด๊ฑธ ์ ๋๋ก ์์์ผ ์ํฉ์ ๋ง๋ ํจ์๋ฅผ ๊ณจ๋ผ ์ธ ์ ์๊ฑฐ๋ . ํ๋์ฉ ํํค์ณ๋ณด์! ๐ฌ
โ re.match() โ ๋ฌธ์์ด ์์๋ถํฐ ๋งค์นญ
re.match()๋ ๋ฌธ์์ด์ ์ฒ์๋ถํฐ ํจํด์ด ์ผ์นํ๋์ง ํ์ธํด.
์ค๊ฐ์ ์๋ ํจํด์ ์ฐพ์ง ๋ชปํ๋ค๋ ์ ์ด ํต์ฌ์ด์ผ!
import re
text1 = "Python is awesome"
text2 = "I love Python"
# match๋ ๋ฌธ์์ด ์์๋ถํฐ ํ์ธ
result1 = re.match(r'Python', text1)
result2 = re.match(r'Python', text2)
print(result1) # <re.Match object; span=(0, 6), match='Python'>
print(result2) # None โ ์์์ด 'Python'์ด ์๋๋๊น!
# ๋งค์นญ ๊ฒฐ๊ณผ ํ์ฉ
if result1:
print(f"๋งค์นญ๋ ๋ฌธ์์ด: {result1.group()}") # Python
print(f"์์ ์์น: {result1.start()}") # 0
print(f"๋ ์์น: {result1.end()}") # 6
print(f"๋ฒ์: {result1.span()}") # (0, 6)
โก re.search() โ ์ ์ฒด ๋ฌธ์์ด์์ ์ฒซ ๋ฒ์งธ ๋งค์นญ
re.search()๋ ๋ฌธ์์ด ์ ์ฒด๋ฅผ ํ์ํด์ ์ฒซ ๋ฒ์งธ๋ก ์ผ์นํ๋ ํจํด์ ์ฐพ์.
re.match()์ ๋ฌ๋ฆฌ ๋ฌธ์์ด ์ค๊ฐ์ ์์ด๋ ์ฐพ์ ์ ์์ด!
import re
text = "์ค๋ ๋ ์จ๋ ๋ง์, ๊ธฐ์จ์ 25๋, ์ต๋๋ 60%"
# ์ฒซ ๋ฒ์งธ ์ซ์ ์ฐพ๊ธฐ
result = re.search(r'\d+', text)
if result:
print(f"์ฒซ ๋ฒ์งธ ์ซ์: {result.group()}") # 25
print(f"์์น: {result.span()}") # (12, 14)
# ์ด๋ฉ์ผ ์ฐพ๊ธฐ
email_text = "์ฐ๋ฝ์ฒ: contact@example.com ์ผ๋ก ๋ฉ์ผ ์ฃผ์ธ์"
email_result = re.search(r'[\w.-]+@[\w.-]+\.\w+', email_text)
if email_result:
print(f"์ด๋ฉ์ผ: {email_result.group()}") # contact@example.com
โข re.findall() โ ๋ชจ๋ ๋งค์นญ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์คํธ๋ก
์ค๋ฌด์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ํจ์ ์ค ํ๋์ผ! ํจํด๊ณผ ์ผ์นํ๋ ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์คํธ๋ก ๋ฐํํด์ค.
import re
log_text = """
2024-01-15 ERROR: ์๋ฒ ์ฐ๊ฒฐ ์คํจ
2024-01-15 INFO: ์ฌ์๋ ์ค...
2024-01-16 WARNING: ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ
2024-01-16 ERROR: ํ์์์ ๋ฐ์
"""
# ๋ชจ๋ ๋ ์ง ์ถ์ถ
dates = re.findall(r'\d{4}-\d{2}-\d{2}', log_text)
print(dates) # ['2024-01-15', '2024-01-15', '2024-01-16', '2024-01-16']
# ๋ชจ๋ ๋ก๊ทธ ๋ ๋ฒจ ์ถ์ถ
levels = re.findall(r'ERROR|WARNING|INFO', log_text)
print(levels) # ['ERROR', 'INFO', 'WARNING', 'ERROR']
# ๊ทธ๋ฃน์ ์ฌ์ฉํ ์ถ์ถ (ํํ ๋ฆฌ์คํธ ๋ฐํ)
pattern = r'(\d{4}-\d{2}-\d{2})\s+(ERROR|WARNING|INFO)'
matches = re.findall(pattern, log_text)
print(matches)
# [('2024-01-15', 'ERROR'), ('2024-01-15', 'INFO'),
# ('2024-01-16', 'WARNING'), ('2024-01-16', 'ERROR')]
๊ทธ๋ฃน์ด 1๊ฐ๋ฉด โ ๊ทธ๋ฃน ๋ด์ฉ์ ๋ฌธ์์ด ๋ฆฌ์คํธ ๋ฐํ
๊ทธ๋ฃน์ด 2๊ฐ ์ด์์ด๋ฉด โ ํํ ๋ฆฌ์คํธ ๋ฐํ
โฃ re.finditer() โ ์ดํฐ๋ ์ดํฐ๋ก ๋งค์นญ ๊ฐ์ฒด ์ํ
re.findall()์ด ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ๋ค๋ฉด, re.finditer()๋ ์ดํฐ๋ ์ดํฐ๋ฅผ ๋ฐํํด.
๋งค์นญ ๊ฐ์ฒด ์์ฒด๋ฅผ ์ํํ ์ ์์ด์ ์์น ์ ๋ณด๊น์ง ํ์ฉํ ๋ ์ ์ฉํด!
import re
text = "์ฌ๊ณผ 3๊ฐ, ๋ฐ๋๋ 5๊ฐ, ํฌ๋ 2๊ฐ๋ฅผ ์์ด์"
# ์ซ์์ ๊ทธ ์์น ์ ๋ณด ํจ๊ป ์ถ์ถ
for match in re.finditer(r'\d+', text):
print(f"์ซ์: {match.group()}, ์์น: {match.span()}")
# ์ถ๋ ฅ:
# ์ซ์: 3, ์์น: (3, 4)
# ์ซ์: 5, ์์น: (10, 11)
# ์ซ์: 2, ์์น: (16, 17)
โค re.sub() โ ํจํด ์นํ์ ๋ํ์
re.sub()์ ํจํด์ ๋งค์นญ๋๋ ๋ถ๋ถ์ ๋ค๋ฅธ ๋ฌธ์์ด๋ก ์นํํด์ค.
๋จ์ ์นํ๋ถํฐ ํจ์๋ฅผ ์ด์ฉํ ๋์ ์นํ๊น์ง ๊ฐ๋ฅํด์ ์ ๋ง ๊ฐ๋ ฅํด!
import re
# ๊ธฐ๋ณธ ์นํ
text = "Hello World Python"
# ์ฌ๋ฌ ๊ณต๋ฐฑ์ ํ๋๋ก
result = re.sub(r'\s+', ' ', text)
print(result) # "Hello World Python"
# ์ ํ๋ฒํธ ํ์ ํต์ผ
phone_text = "์ฐ๋ฝ์ฒ: 010-1234-5678, 010.9876.5432, 01011112222"
# ๋ชจ๋ ์ ํ๋ฒํธ๋ฅผ XXX-XXXX-XXXX ํ์์ผ๋ก
normalized = re.sub(r'(\d{3})[-.]?(\d{4})[-.]?(\d{4})', r'\1-\2-\3', phone_text)
print(normalized) # "์ฐ๋ฝ์ฒ: 010-1234-5678, 010-9876-5432, 010-1111-2222"
# ํจ์๋ฅผ ์ด์ฉํ ๋์ ์นํ (๊ฐ๋ ฅ!)
def double_number(match):
return str(int(match.group()) * 2)
text2 = "์ฌ๊ณผ 3๊ฐ, ๋ฐ๋๋ 5๊ฐ, ํฌ๋ 2๊ฐ"
result2 = re.sub(r'\d+', double_number, text2)
print(result2) # "์ฌ๊ณผ 6๊ฐ, ๋ฐ๋๋ 10๊ฐ, ํฌ๋ 4๊ฐ"
# count ํ๋ผ๋ฏธํฐ๋ก ์นํ ํ์ ์ ํ
text3 = "aaa bbb aaa ccc aaa"
result3 = re.sub(r'aaa', 'XXX', text3, count=2)
print(result3) # "XXX bbb XXX ccc aaa"
โฅ re.split() โ ํจํด์ผ๋ก ๋ฌธ์์ด ๋ถ๋ฆฌ
import re
# ๋ค์ํ ๊ตฌ๋ถ์๋ก ๋ถ๋ฆฌ
text = "์ฌ๊ณผ,๋ฐ๋๋;ํฌ๋ ๋ธ๊ธฐ|์๋ฐ"
result = re.split(r'[,;\s|]+', text)
print(result) # ['์ฌ๊ณผ', '๋ฐ๋๋', 'ํฌ๋', '๋ธ๊ธฐ', '์๋ฐ']
# ๊ตฌ๋ถ์๋ ๊ฒฐ๊ณผ์ ํฌํจ์ํค๊ธฐ (๊ทธ๋ฃน ์ฌ์ฉ)
text2 = "one1two2three3four"
result2 = re.split(r'(\d)', text2)
print(result2) # ['one', '1', 'two', '2', 'three', '3', 'four']
# maxsplit์ผ๋ก ๋ถ๋ฆฌ ํ์ ์ ํ
result3 = re.split(r'\d', text2, maxsplit=2)
print(result3) # ['one', 'two', 'three3four']
| ํจ์ | ๋ฐํ๊ฐ | ์ฃผ์ ์ฉ๋ |
|---|---|---|
re.match() | Match ๊ฐ์ฒด / None | ๋ฌธ์์ด ์์ ํจํด ํ์ธ |
re.search() | Match ๊ฐ์ฒด / None | ์ฒซ ๋ฒ์งธ ํจํด ํ์ |
re.findall() | ๋ฆฌ์คํธ | ๋ชจ๋ ๋งค์นญ ์ถ์ถ |
re.finditer() | ์ดํฐ๋ ์ดํฐ | ์์น ์ ๋ณด ํฌํจ ์ํ |
re.sub() | ๋ฌธ์์ด | ํจํด ์นํ |
re.split() | ๋ฆฌ์คํธ | ํจํด์ผ๋ก ๋ถ๋ฆฌ |
re.compile() | Pattern ๊ฐ์ฒด | ํจํด ์ปดํ์ผ (์ฌ์ฌ์ฉ) |
๐งฉ 3. ์ ๊ทํํ์ ํจํด ๋ฌธ๋ฒ โ ์ค์ ๋ ์ํผ
์ด์ ์ง์ง ํต์ฌ์ด์ผ! ํจํด ๋ฌธ๋ฒ์ ์ ๋๋ก ์์์ผ ์ ๊ทํํ์์ ์์ ์์ฌ๋ก ์ธ ์ ์์ด.
์ธ์ธ ํ์ ์์ด. ์ดํดํ๋ฉด ์์ฐ์ค๋ฝ๊ฒ ์์ ์ต๊ฑฐ๋ . ๐
๐ค ๋ฌธ์ ํด๋์ค (Character Classes)
import re
text = "Hello World 123 !@#"
# \d : ์ซ์ [0-9]
print(re.findall(r'\d', text)) # ['1', '2', '3']
# \D : ์ซ์๊ฐ ์๋ ๊ฒ
print(re.findall(r'\D+', text)) # ['Hello World ', ' !@#']
# \w : ๋จ์ด ๋ฌธ์ [a-zA-Z0-9_]
print(re.findall(r'\w+', text)) # ['Hello', 'World', '123']
# \W : ๋จ์ด ๋ฌธ์๊ฐ ์๋ ๊ฒ
print(re.findall(r'\W+', text)) # [' ', ' ', ' !@#']
# \s : ๊ณต๋ฐฑ ๋ฌธ์ (์คํ์ด์ค, ํญ, ์ค๋ฐ๊ฟ ๋ฑ)
print(re.findall(r'\s+', text)) # [' ', ' ', ' ']
# \S : ๊ณต๋ฐฑ์ด ์๋ ๊ฒ
print(re.findall(r'\S+', text)) # ['Hello', 'World', '123', '!@#']
# . : ์ค๋ฐ๊ฟ ์ ์ธ ๋ชจ๋ ๋ฌธ์
print(re.findall(r'.', "ab\nc")) # ['a', 'b', 'c'] โ \n ์ ์ธ!
๐ ์๋์ (Quantifiers)
import re
text = "color colour colouur"
# * : 0ํ ์ด์
print(re.findall(r'colou*r', text)) # ['color', 'colour', 'colouur']
# + : 1ํ ์ด์
print(re.findall(r'colou+r', text)) # ['colour', 'colouur']
# ? : 0ํ ๋๋ 1ํ
print(re.findall(r'colou?r', text)) # ['color', 'colour']
# {n} : ์ ํํ nํ
print(re.findall(r'colou{2}r', text)) # ['colouur']
# {n,m} : nํ ์ด์ mํ ์ดํ
print(re.findall(r'colou{1,2}r', text)) # ['colour', 'colouur']
# {n,} : nํ ์ด์
print(re.findall(r'colou{1,}r', text)) # ['colour', 'colouur']
?๋ฅผ ์๋์ ๋ค์ ๋ถ์ด๋ฉด ๋นํ์์ ์ด ๋ผ โ ๊ฐ๋ฅํ ํ ์ ๊ฒ ๋งค์นญํด.
import re
html = "<b>๊ตต๊ฒ</b> ๊ทธ๋ฆฌ๊ณ <i>๊ธฐ์ธ์</i>"
# ํ์์ ๋งค์นญ (greedy) - ๋๋ฌด ๋ง์ด ์ก์๋ฒ๋ฆผ
greedy = re.findall(r'<.+>', html)
print(greedy) # ['<b>๊ตต๊ฒ</b> ๊ทธ๋ฆฌ๊ณ <i>๊ธฐ์ธ์</i>'] โ ์ ์ฒด๋ฅผ ํ๋๋ก!
# ๋นํ์์ ๋งค์นญ (non-greedy) - ๋ฑ ํ์ํ ๋งํผ๋ง
non_greedy = re.findall(r'<.+?>', html)
print(non_greedy) # ['<b>', '</b>', '<i>', '</i>'] โ ๊ฐ ํ๊ทธ๋ฅผ ๋ฐ๋ก!
๐ฏ ์ต์ปค (Anchors) โ ์์น๋ฅผ ์ง์ ํด
import re
# ^ : ๋ฌธ์์ด(๋๋ ์ค)์ ์์
print(re.findall(r'^\d+', "123abc")) # ['123']
print(re.findall(r'^\d+', "abc123")) # [] โ ์์์ด ์ซ์๊ฐ ์๋
# $ : ๋ฌธ์์ด(๋๋ ์ค)์ ๋
print(re.findall(r'\d+$', "abc123")) # ['123']
print(re.findall(r'\d+$', "123abc")) # [] โ ๋์ด ์ซ์๊ฐ ์๋
# \b : ๋จ์ด ๊ฒฝ๊ณ
text = "cat concatenate cats"
print(re.findall(r'\bcat\b', text)) # ['cat'] โ ์ ํํ 'cat'๋ง
print(re.findall(r'cat', text)) # ['cat', 'cat', 'cat'] โ ๋ชจ๋ ํฌํจ
# \B : ๋จ์ด ๊ฒฝ๊ณ๊ฐ ์๋ ๊ณณ
print(re.findall(r'\Bcat\B', "concatenate")) # ['cat'] โ ๋จ์ด ์ค๊ฐ์ cat
๐๏ธ ๊ทธ๋ฃน (Groups) โ ํจํด์ ๋ฌถ์ด์ ํ์ฉ
๊ทธ๋ฃน์ ์ ๊ทํํ์์ ๊ฝ์ด๋ผ๊ณ ํ ์ ์์ด! ํจํด์ ๋ฌถ์ด์ ์ถ์ถํ๊ฑฐ๋, ์ญ์ฐธ์กฐํ๊ฑฐ๋, ์ด๋ฆ์ ๋ถ์ผ ์ ์์ด.
import re
# ๊ธฐ๋ณธ ๊ทธ๋ฃน ()
text = "2024-01-15"
match = re.match(r'(\d{4})-(\d{2})-(\d{2})', text)
if match:
print(match.group(0)) # '2024-01-15' (์ ์ฒด)
print(match.group(1)) # '2024' (์ฒซ ๋ฒ์งธ ๊ทธ๋ฃน)
print(match.group(2)) # '01' (๋ ๋ฒ์งธ ๊ทธ๋ฃน)
print(match.group(3)) # '15' (์ธ ๋ฒ์งธ ๊ทธ๋ฃน)
print(match.groups()) # ('2024', '01', '15')
# ์ด๋ฆ ์๋ ๊ทธ๋ฃน (?P<name>...)
match2 = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', text)
if match2:
print(match2.group('year')) # '2024'
print(match2.group('month')) # '01'
print(match2.group('day')) # '15'
print(match2.groupdict()) # {'year': '2024', 'month': '01', 'day': '15'}
# ๋น์บก์ฒ ๊ทธ๋ฃน (?:...) - ๊ทธ๋ฃนํ๋ ํ๋ ์บก์ฒ๋ ์ ํจ
text3 = "Mr. Smith and Mrs. Jones"
result = re.findall(r'(?:Mr|Mrs)\. (\w+)', text3)
print(result) # ['Smith', 'Jones'] โ Mr/Mrs๋ ์บก์ฒ ์ ๋จ
# ์ญ์ฐธ์กฐ (Backreference) - ๊ฐ์ ํจํด ๋ฐ๋ณต ์ฐพ๊ธฐ
text4 = "hello hello world world"
result4 = re.findall(r'\b(\w+) \1\b', text4)
print(result4) # ['hello', 'world'] โ ์ฐ์์ผ๋ก ๋ฐ๋ณต๋ ๋จ์ด!
๐ ๋ฃฉ์ดํค๋ & ๋ฃฉ๋นํ์ธ๋ โ ์ ๋ก ๋๋น ์ด์์
์ด๊ฑด ์ข ๊ณ ๊ธ ๊ธฐ์ ์ธ๋ฐ, ์๋ฉด ์ ๋ง ๊ฐ๋ ฅํด! ๋งค์นญ ๊ฒฐ๊ณผ์๋ ํฌํจ๋์ง ์์ผ๋ฉด์ ์กฐ๊ฑด๋ง ๊ฒ์ฌํ๋ ๊ธฐ๋ฒ์ด์ผ.
import re
# ๊ธ์ ๋ฃฉ์ดํค๋ (?=...) - ๋ค์ ํน์ ํจํด์ด ์ค๋ ๊ฒฝ์ฐ
prices = "์ฌ๊ณผ 1500์, ๋ฐ๋๋ 800์, ํฌ๋ 3000์"
# '์' ์์ ์ซ์๋ง ์ถ์ถ
numbers = re.findall(r'\d+(?=์)', prices)
print(numbers) # ['1500', '800', '3000']
# ๋ถ์ ๋ฃฉ์ดํค๋ (?!...) - ๋ค์ ํน์ ํจํด์ด ์ค์ง ์๋ ๊ฒฝ์ฐ
text = "file.txt file.py file.jpg"
# .py๊ฐ ์๋ ํ์ฅ์ ์ถ์ถ
exts = re.findall(r'\.\w+(?!py)\b', text)
print(exts) # ['.txt', '.jpg']
# ๊ธ์ ๋ฃฉ๋นํ์ธ๋ (?<=...) - ์์ ํน์ ํจํด์ด ์ค๋ ๊ฒฝ์ฐ
text2 = "๊ฐ๊ฒฉ: $100, ํ ์ธ: $30, ์ด์ก: $70"
# $ ๋ค์ ์ซ์๋ง ์ถ์ถ
amounts = re.findall(r'(?<=\$)\d+', text2)
print(amounts) # ['100', '30', '70']
# ๋ถ์ ๋ฃฉ๋นํ์ธ๋ (?<!...) - ์์ ํน์ ํจํด์ด ์ค์ง ์๋ ๊ฒฝ์ฐ
text3 = "un-happy, un-lucky, lucky, happy"
# 'un-' ๋ค์ ์ค์ง ์๋ ๋จ์ด๋ง
words = re.findall(r'(?<!un-)\b\w+\b', text3)
print(words) # ['un', 'un', 'lucky', 'happy'] โ ์ฃผ์: 'un'๋ ํฌํจ๋จ
โก 4. re.compile() โ ์ฑ๋ฅ์ ์ํ ํจํด ์ปดํ์ผ
๊ฐ์ ํจํด์ ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํ๋ค๋ฉด re.compile()๋ก ๋ฏธ๋ฆฌ ์ปดํ์ผํด๋๋ ๊ฒ ํจ์ฌ ํจ์จ์ ์ด์ผ!
ํจํด์ ๋งค๋ฒ ํ์ฑํ๋ ์ค๋ฒํค๋๋ฅผ ์ค์ผ ์ ์๊ฑฐ๋ . ์ค๋ฌด์์ ๋์ฉ๋ ํ
์คํธ ์ฒ๋ฆฌํ ๋ ํนํ ์ค์ํด.
import re
import time
# ์ปดํ์ผ ์์ด ์ฌ์ฉ
start = time.time()
text_list = ["์ด๋ฉ์ผ: user@example.com"] * 100000
for text in text_list:
re.findall(r'[\w.-]+@[\w.-]+\.\w+', text)
print(f"์ปดํ์ผ ์์ด: {time.time() - start:.3f}์ด")
# ์ปดํ์ผ ํ ์ฌ์ฉ
email_pattern = re.compile(r'[\w.-]+@[\w.-]+\.\w+')
start = time.time()
for text in text_list:
email_pattern.findall(text)
print(f"์ปดํ์ผ ํ: {time.time() - start:.3f}์ด")
# ์ปดํ์ผ ํ๊ฐ ํจ์ฌ ๋น ๋ฆ!
# ์ปดํ์ผ๋ ํจํด ๊ฐ์ฒด์ ๋ฉ์๋๋ค
pattern = re.compile(r'(\d{4})-(\d{2})-(\d{2})')
# ๋์ผํ ๋ฉ์๋๋ค์ ์ฌ์ฉํ ์ ์์ด
result = pattern.match("2024-01-15 ์ค๋ ๋ ์ง")
print(result.groups()) # ('2024', '01', '15')
result2 = pattern.findall("2024-01-15 ~ 2024-12-31")
print(result2) # [('2024', '01', '15'), ('2024', '12', '31')]
๐ฉ ํ๋๊ทธ (Flags) โ ๋งค์นญ ๋์ ์ ์ด
ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๊ทํํ์์ ๋์ ๋ฐฉ์์ ์ธ๋ฐํ๊ฒ ์ ์ดํ ์ ์์ด!
import re
# re.IGNORECASE (re.I) - ๋์๋ฌธ์ ๋ฌด์
text = "Python python PYTHON"
result = re.findall(r'python', text, re.IGNORECASE)
print(result) # ['Python', 'python', 'PYTHON']
# re.MULTILINE (re.M) - ์ฌ๋ฌ ์ค ๋ชจ๋ (^, $๊ฐ ๊ฐ ์ค์ ์ ์ฉ)
multiline_text = """์ฒซ ๋ฒ์งธ ์ค
๋ ๋ฒ์งธ ์ค
์ธ ๋ฒ์งธ ์ค"""
# MULTILINE ์์ด - ์ ์ฒด ๋ฌธ์์ด์ ์์๋ง
result1 = re.findall(r'^\w+', multiline_text)
print(result1) # ['์ฒซ']
# MULTILINE ์์ด - ๊ฐ ์ค์ ์์
result2 = re.findall(r'^\w+', multiline_text, re.MULTILINE)
print(result2) # ['์ฒซ', '๋', '์ธ']
# re.DOTALL (re.S) - . ์ด ์ค๋ฐ๊ฟ๋ ํฌํจ
text2 = "์์\n์ค๊ฐ\n๋"
print(re.findall(r'์์.+๋', text2)) # [] โ . ์ด \n ๋ชป ๋์
print(re.findall(r'์์.+๋', text2, re.DOTALL)) # ['์์\n์ค๊ฐ\n๋']
# re.VERBOSE (re.X) - ์ฃผ์๊ณผ ๊ณต๋ฐฑ ํ์ฉ (๋ณต์กํ ํจํด ๊ฐ๋
์ฑ ํฅ์!)
email_pattern = re.compile(r'''
[\w.+-]+ # ๋ก์ปฌ ํํธ (์ฌ์ฉ์๋ช
)
@ # @ ๊ธฐํธ
[\w-]+ # ๋๋ฉ์ธ ์ด๋ฆ
(?:\.[\w-]+)* # ์๋ธ๋๋ฉ์ธ (์ ํ์ )
\.\w{2,} # ์ต์์ ๋๋ฉ์ธ (.com, .kr ๋ฑ)
''', re.VERBOSE)
emails = ["user@example.com", "test.email+tag@sub.domain.co.kr"]
for email in emails:
if email_pattern.match(email):
print(f"โ
์ ํจํ ์ด๋ฉ์ผ: {email}")
# ์ฌ๋ฌ ํ๋๊ทธ ์กฐํฉ (| ์ฌ์ฉ)
pattern = re.compile(r'python', re.IGNORECASE | re.MULTILINE)
re.VERBOSE ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฃผ์์ ๋ฌ ์ ์์ด์ ๋์ค์ ์ฝ๋๋ฅผ ์ฝ์ ๋ ํจ์ฌ ์ดํดํ๊ธฐ ์ฌ์. ํ ํ๋ก์ ํธ์์ ํนํ ์ ์ฉํด!
๐ ๏ธ 5. ์ค์ ํจํด ๋ ์ํผ โ ์์ฃผ ์ฐ๋ ์ ๊ทํํ์ ๋ชจ์
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋๊น, ์ด์ ์ค์ ๋ก ์์ฃผ ์ฐ์ด๋ ํจํด๋ค์ ๋ชจ์๋ดค์ด!
๋ณต๋ถํด์ ๋ฐ๋ก ์ธ ์ ์๋ ์ค์ฉ์ ์ธ ๋ ์ํผ๋ค์ด์ผ. ๐ณ
import re
# ===== ์ค์ ์์ ๋ชจ์ =====
# 1. ์ด๋ฉ์ผ ์ ํจ์ฑ ๊ฒ์ฌ
def validate_email(email):
pattern = re.compile(r'^[\w.+-]+@[\w-]+(?:\.[\w-]+)*\.\w{2,}$')
return bool(pattern.match(email))
print(validate_email("user@example.com")) # True
print(validate_email("invalid.email")) # False
print(validate_email("test@sub.domain.co.kr")) # True
# 2. ์ ํ๋ฒํธ ์ ๊ทํ
def normalize_phone(phone):
# ์ซ์๋ง ์ถ์ถ
digits = re.sub(r'\D', '', phone)
# ํ์ ์ ์ฉ
if len(digits) == 11: # 010-XXXX-XXXX
return re.sub(r'(\d{3})(\d{4})(\d{4})', r'\1-\2-\3', digits)
elif len(digits) == 10: # 02-XXXX-XXXX ๋๋ 0XX-XXX-XXXX
return re.sub(r'(\d{2,3})(\d{3,4})(\d{4})', r'\1-\2-\3', digits)
return phone
print(normalize_phone("010.1234.5678")) # 010-1234-5678
print(normalize_phone("01012345678")) # 010-1234-5678
print(normalize_phone("02-123-4567")) # 02-123-4567
# 3. HTML ํ๊ทธ ์ ๊ฑฐ
def strip_html(html_text):
clean = re.sub(r'<[^>]+>', '', html_text)
# ์ฐ์ ๊ณต๋ฐฑ ์ ๋ฆฌ
clean = re.sub(r'\s+', ' ', clean).strip()
return clean
html = "<h1>์ ๋ชฉ</h1><p>์ด๊ฒ์ <b>๊ตต์</b> ํ
์คํธ์
๋๋ค.</p>"
print(strip_html(html)) # "์ ๋ชฉ ์ด๊ฒ์ ๊ตต์ ํ
์คํธ์
๋๋ค."
# 4. ๊ฐ์ธ์ ๋ณด ๋ง์คํน
def mask_personal_info(text):
# ์ด๋ฉ์ผ ๋ง์คํน
text = re.sub(r'([\w.+-]{2})[\w.+-]*(@[\w.-]+)', r'\1***\2', text)
# ์ ํ๋ฒํธ ๋ง์คํน
text = re.sub(r'(\d{3})-(\d{4})-(\d{4})', r'\1-****-\3', text)
return text
personal_text = "์ฐ๋ฝ์ฒ: 010-1234-5678, ์ด๋ฉ์ผ: username@example.com"
print(mask_personal_info(personal_text))
# "์ฐ๋ฝ์ฒ: 010-****-5678, ์ด๋ฉ์ผ: us***@example.com"
# 5. ํ๊ธ๋ง ์ถ์ถ
def extract_korean(text):
return re.findall(r'[๊ฐ-ํฃ]+', text)
mixed = "Hello ์๋
ํ์ธ์ World ๋ฐ๊ฐ์ต๋๋ค 123"
print(extract_korean(mixed)) # ['์๋
ํ์ธ์', '๋ฐ๊ฐ์ต๋๋ค']
๐ฌ 6. ๊ณ ๊ธ ๋ฌธ์์ด ์กฐ์ โ Python ๋ด์ฅ ๊ธฐ๋ฅ ์์ ํ์ฉ
์ ๊ทํํ์๋ง์ด ๋ต์ ์๋์ผ! Python์ ๋ด์ฅ ๋ฌธ์์ด ๋ฉ์๋๋ค๋ ์์ฒญ ๊ฐ๋ ฅํ๊ฑฐ๋ .
์ ๊ทํํ์๊ณผ ๋ด์ฅ ๋ฉ์๋๋ฅผ ์ ์ ํ ์กฐํฉํ๋ฉด ์ต๊ฐ์ ํ
์คํธ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํด. ๐ช
๐ str.translate() โ ๋ฌธ์ ๋จ์ ๋ณํ์ ์ต๊ฐ์
str.translate()๋ ๋ฌธ์ ๋จ์๋ก ๋ณํํ๊ฑฐ๋ ์ญ์ ํ ๋ ์ ๊ทํํ์๋ณด๋ค ํจ์ฌ ๋น ๋ฅธ ๊ฒฝ์ฐ๊ฐ ๋ง์!
# str.maketrans() + str.translate() ์กฐํฉ
# ํน์๋ฌธ์ ์ ๊ฑฐ
text = "Hello, World! How are you? I'm fine."
# ์ ๊ฑฐํ ๋ฌธ์๋ค ์ง์
remove_chars = ",.!?'"
table = str.maketrans('', '', remove_chars)
result = text.translate(table)
print(result) # "Hello World How are you Im fine"
# ๋ฌธ์ ์นํ
# ์์ด ์๋ฌธ์๋ฅผ ๋๋ฌธ์๋ก, ํน์๋ฌธ์ ์ ๊ฑฐ
table2 = str.maketrans(
'abcdefghijklmnopqrstuvwxyz', # ๋ณํ ์
'ABCDEFGHIJKLMNOPQRSTUVWXYZ', # ๋ณํ ํ
',.!?' # ์ญ์ ํ ๋ฌธ์
)
result2 = text.translate(table2)
print(result2) # "HELLO WORLD HOW ARE YOU I'M FINE"
# ์ ๋์ฝ๋ ํ์ฉ - ์ ๊ฐ ๋ฌธ์๋ฅผ ๋ฐ๊ฐ์ผ๋ก
fullwidth = "๏ผก๏ผข๏ผฃ๏ผค๏ผฅ๏ผ๏ผ๏ผ๏ผ๏ผ"
table3 = str.maketrans(
''.join(chr(0xFF01 + i) for i in range(94)), # ์ ๊ฐ ๋ฌธ์
''.join(chr(0x21 + i) for i in range(94)) # ๋ฐ๊ฐ ๋ฌธ์
)
print(fullwidth.translate(table3)) # "ABCDE12345"
โ๏ธ ๊ณ ๊ธ ์ฌ๋ผ์ด์ฑ๊ณผ ๋ฌธ์์ด ๋ถ๋ฆฌ
# ๋ค์ํ ๋ฌธ์์ด ๋ถ๋ฆฌ ๊ธฐ๋ฒ
# partition() - ๊ตฌ๋ถ์ ๊ธฐ์ค์ผ๋ก 3๋ถํ
text = "์ด๋ฆ: ํ๊ธธ๋"
before, sep, after = text.partition(': ')
print(before) # "์ด๋ฆ"
print(sep) # ": "
print(after) # "ํ๊ธธ๋"
# rpartition() - ์ค๋ฅธ์ชฝ์์ ์ฒซ ๋ฒ์งธ ๊ตฌ๋ถ์ ๊ธฐ์ค
path = "/home/user/documents/file.txt"
dir_path, sep, filename = path.rpartition('/')
print(dir_path) # "/home/user/documents"
print(filename) # "file.txt"
# splitlines() - ์ค ๋จ์ ๋ถ๋ฆฌ
multiline = "์ฒซ ๋ฒ์งธ ์ค\n๋ ๋ฒ์งธ ์ค\r\n์ธ ๋ฒ์งธ ์ค\r๋ค ๋ฒ์งธ ์ค"
lines = multiline.splitlines()
print(lines)
# ['์ฒซ ๋ฒ์งธ ์ค', '๋ ๋ฒ์งธ ์ค', '์ธ ๋ฒ์งธ ์ค', '๋ค ๋ฒ์งธ ์ค']
# keepends=True๋ก ์ค๋ฐ๊ฟ ๋ฌธ์ ์ ์ง
lines_with_ends = multiline.splitlines(keepends=True)
print(lines_with_ends)
# ['์ฒซ ๋ฒ์งธ ์ค\n', '๋ ๋ฒ์งธ ์ค\r\n', '์ธ ๋ฒ์งธ ์ค\r', '๋ค ๋ฒ์งธ ์ค']
# split() ๊ณ ๊ธ ํ์ฉ
csv_line = "ํ๊ธธ๋,25,์์ธ,๊ฐ๋ฐ์"
fields = csv_line.split(',')
print(fields) # ['ํ๊ธธ๋', '25', '์์ธ', '๊ฐ๋ฐ์']
# maxsplit์ผ๋ก ๋ถ๋ฆฌ ํ์ ์ ํ
limited = csv_line.split(',', maxsplit=2)
print(limited) # ['ํ๊ธธ๋', '25', '์์ธ,๊ฐ๋ฐ์']
๐จ ๋ฌธ์์ด ํฌ๋งคํ ๊ณ ๊ธ ๊ธฐ๋ฒ
# f-string ๊ณ ๊ธ ํ์ฉ (Python 3.6+)
name = "ํ๊ธธ๋"
score = 95.678
items = [1, 2, 3, 4, 5]
# ๊ธฐ๋ณธ ํฌ๋งคํ
print(f"์ด๋ฆ: {name}, ์ ์: {score:.2f}") # "์ด๋ฆ: ํ๊ธธ๋, ์ ์: 95.68"
# ์ ๋ ฌ๊ณผ ํจ๋ฉ
print(f"{'์ผ์ชฝ':<10}|{'๊ฐ์ด๋ฐ':^10}|{'์ค๋ฅธ์ชฝ':>10}")
# "์ผ์ชฝ | ๊ฐ์ด๋ฐ | ์ค๋ฅธ์ชฝ"
# ์ซ์ ํฌ๋งคํ
big_num = 1234567890
print(f"{big_num:,}") # "1,234,567,890" (์ฒ ๋จ์ ๊ตฌ๋ถ)
print(f"{big_num:010d}") # "1234567890" (0 ํจ๋ฉ)
print(f"{0.1234:.2%}") # "12.34%" (ํผ์ผํธ)
print(f"{255:#010x}") # "0x000000ff" (16์ง์)
print(f"{255:#010b}") # "0b011111111" (2์ง์)
# ํํ์ ์ฌ์ฉ
print(f"ํฉ๊ณ: {sum(items)}, ํ๊ท : {sum(items)/len(items):.1f}")
# Python 3.8+ - = ๋๋ฒ๊น
์ถ๋ ฅ
x = 42
print(f"{x=}") # "x=42" โ ๋ณ์๋ช
๊ณผ ๊ฐ์ ํจ๊ป ์ถ๋ ฅ!
# format() ๋ฉ์๋ ๊ณ ๊ธ ํ์ฉ
template = "{name}๋์ {subject} ์ ์๋ {score:.1f}์ ์
๋๋ค."
print(template.format(name="๊น์ฒ ์", subject="์ํ", score=87.5))
# ๋์
๋๋ฆฌ ์ธํจํน
data = {"name": "์ด์ํฌ", "subject": "์์ด", "score": 92.3}
print(template.format(**data))
๐ ๋ฌธ์์ด ๊ฒ์๊ณผ ๋ถ์
# ๊ณ ๊ธ ๋ฌธ์์ด ๊ฒ์ ๊ธฐ๋ฒ
text = "Python์ ์ ๋ง ๊ฐ๋ ฅํ ์ธ์ด์ผ. Python์ ๋ฐฐ์ฐ๋ฉด ๋ญ๋ ํ ์ ์์ด!"
# count() - ์ถํ ํ์
print(text.count('Python')) # 2
print(text.count('Python', 0, 20)) # 1 (๋ฒ์ ์ง์ )
# find() vs index()
print(text.find('๊ฐ๋ ฅ')) # 10 (๋ชป ์ฐพ์ผ๋ฉด -1 ๋ฐํ)
print(text.find('์๋๋จ์ด')) # -1
# print(text.index('์๋๋จ์ด')) # ValueError ๋ฐ์!
# rfind() - ์ค๋ฅธ์ชฝ์์ ๊ฒ์
print(text.rfind('Python')) # ๋ง์ง๋ง Python์ ์์น
# startswith(), endswith() - ์ ๋์ฌ/์ ๋ฏธ์ฌ ํ์ธ
print(text.startswith('Python')) # True
print(text.endswith('์์ด!')) # True
# ํํ๋ก ์ฌ๋ฌ ํจํด ๋์ ํ์ธ
filenames = ["report.pdf", "data.csv", "image.png", "script.py"]
for f in filenames:
if f.endswith(('.pdf', '.csv')):
print(f"๋ฌธ์ ํ์ผ: {f}")
# ๋ฌธ์ ํ์ผ: report.pdf
# ๋ฌธ์ ํ์ผ: data.csv
# ๋ฌธ์์ด ๋ถ์ ๋ฉ์๋๋ค
sample = "Hello World 123"
print(sample.isalpha()) # False (์ซ์ ํฌํจ)
print(sample.isdigit()) # False (๋ฌธ์ ํฌํจ)
print(sample.isalnum()) # False (๊ณต๋ฐฑ ํฌํจ)
print("Hello".isalpha()) # True
print("12345".isdigit()) # True
print("Hello123".isalnum()) # True
print(" ".isspace()) # True
print("HELLO".isupper()) # True
print("hello".islower()) # True
print("Hello World".istitle()) # True
๐ 7. ์ค์ ํ๋ก์ ํธ โ ํ ์คํธ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
์ด์ ๋ฐฐ์ด ๊ฒ๋ค์ ์ข
ํฉํด์ ์ค์ ํ๋ก์ ํธ์์ ์ธ ์ ์๋ ํ
์คํธ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ์ ๋ง๋ค์ด๋ณด์!
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ฌ์ฉ์ ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ ์ ํ๋ ์๋๋ฆฌ์ค๋ฅผ ์๋ก ๋ค์ด๋ณผ๊ฒ. ๐
๐ ๋ก๊ทธ ํ์ผ ๋ถ์๊ธฐ
import re
from collections import Counter, defaultdict
from datetime import datetime
class LogAnalyzer:
"""์๋ฒ ๋ก๊ทธ ํ์ผ ๋ถ์๊ธฐ"""
# ํจํด ๋ฏธ๋ฆฌ ์ปดํ์ผ (์ฑ๋ฅ ์ต์ ํ)
LOG_PATTERN = re.compile(
r'(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+'
r'(?P<level>DEBUG|INFO|WARNING|ERROR|CRITICAL)\s+'
r'(?P<message>.+)',
re.MULTILINE
)
IP_PATTERN = re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b')
URL_PATTERN = re.compile(r'https?://[\w.-]+(?:/[\w./?=%&-]*)?')
def __init__(self, log_text):
self.log_text = log_text
self.entries = self._parse()
def _parse(self):
"""๋ก๊ทธ ํ์ฑ"""
entries = []
for match in self.LOG_PATTERN.finditer(self.log_text):
entries.append({
'timestamp': match.group('timestamp'),
'level': match.group('level'),
'message': match.group('message').strip()
})
return entries
def get_level_stats(self):
"""๋ ๋ฒจ๋ณ ํต๊ณ"""
return Counter(entry['level'] for entry in self.entries)
def get_errors(self):
"""์๋ฌ ๋ก๊ทธ๋ง ์ถ์ถ"""
return [e for e in self.entries if e['level'] in ('ERROR', 'CRITICAL')]
def extract_ips(self):
"""๋ชจ๋ IP ์ฃผ์ ์ถ์ถ"""
ips = []
for entry in self.entries:
ips.extend(self.IP_PATTERN.findall(entry['message']))
return Counter(ips)
def get_hourly_stats(self):
"""์๊ฐ๋๋ณ ๋ก๊ทธ ํต๊ณ"""
hourly = defaultdict(int)
for entry in self.entries:
hour = entry['timestamp'][11:13] # HH ์ถ์ถ
hourly[hour] += 1
return dict(sorted(hourly.items()))
def generate_report(self):
"""๋ถ์ ๋ฆฌํฌํธ ์์ฑ"""
level_stats = self.get_level_stats()
errors = self.get_errors()
top_ips = self.extract_ips().most_common(5)
report = f"""
=== ๋ก๊ทธ ๋ถ์ ๋ฆฌํฌํธ ===
์ด ๋ก๊ทธ ์: {len(self.entries)}๊ฐ
[๋ ๋ฒจ๋ณ ํต๊ณ]
{chr(10).join(f' {level}: {count}๊ฑด' for level, count in level_stats.items())}
[์๋ฌ ๋ชฉ๋ก] ({len(errors)}๊ฑด)
{chr(10).join(f' [{e["timestamp"]}] {e["message"]}' for e in errors[:5])}
[์์ IP ์ฃผ์]
{chr(10).join(f' {ip}: {count}ํ' for ip, count in top_ips)}
"""
return report.strip()
# ์ฌ์ฉ ์์
sample_log = """
2024-01-15 09:00:01 INFO ์๋ฒ ์์ ์๋ฃ
2024-01-15 09:00:15 INFO ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ: 192.168.1.100
2024-01-15 09:01:23 WARNING ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ 80% ์ด๊ณผ
2024-01-15 09:02:45 ERROR ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์คํจ: 192.168.1.200
2024-01-15 09:03:12 INFO ์ฌ์ฐ๊ฒฐ ์๋ ์ค...
2024-01-15 09:03:45 ERROR ํ์์์: 192.168.1.200
2024-01-15 09:05:00 INFO ์ฐ๊ฒฐ ๋ณต๊ตฌ ์๋ฃ
2024-01-15 10:00:00 CRITICAL ๋์คํฌ ์ฉ๋ ๋ถ์กฑ: 192.168.1.100
"""
analyzer = LogAnalyzer(sample_log)
print(analyzer.generate_report())
๐งน ํ ์คํธ ์ ์ ํ์ดํ๋ผ์ธ
import re
import unicodedata
class TextCleaner:
"""ํ
์คํธ ์ ์ ํ์ดํ๋ผ์ธ"""
def __init__(self):
# ํจํด ์ปดํ์ผ
self._html_tag = re.compile(r'<[^>]+>')
self._url = re.compile(r'https?://\S+|www\.\S+')
self._email = re.compile(r'[\w.+-]+@[\w-]+\.\w+')
self._phone = re.compile(r'\d{2,3}[-.]?\d{3,4}[-.]?\d{4}')
self._whitespace = re.compile(r'\s+')
self._special_chars = re.compile(r'[^\w\s๊ฐ-ํฃ.,!?]')
self._repeated_chars = re.compile(r'(.)\1{2,}') # 3ํ ์ด์ ๋ฐ๋ณต
self._emoticons = re.compile(
r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF'
r'\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF]+'
)
def remove_html(self, text):
"""HTML ํ๊ทธ ์ ๊ฑฐ"""
return self._html_tag.sub(' ', text)
def remove_urls(self, text):
"""URL ์ ๊ฑฐ"""
return self._url.sub('[URL]', text)
def mask_personal_info(self, text):
"""๊ฐ์ธ์ ๋ณด ๋ง์คํน"""
text = self._email.sub('[์ด๋ฉ์ผ]', text)
text = self._phone.sub('[์ ํ๋ฒํธ]', text)
return text
def normalize_whitespace(self, text):
"""๊ณต๋ฐฑ ์ ๊ทํ"""
return self._whitespace.sub(' ', text).strip()
def normalize_unicode(self, text):
"""์ ๋์ฝ๋ ์ ๊ทํ (์ ๊ฐโ๋ฐ๊ฐ ๋ฑ)"""
return unicodedata.normalize('NFKC', text)
def reduce_repeated_chars(self, text):
"""๋ฐ๋ณต ๋ฌธ์ ์ถ์ (ใ
ใ
ใ
ใ
โ ใ
ใ
)"""
return self._repeated_chars.sub(r'\1\1', text)
def remove_emoticons(self, text):
"""์ด๋ชจํฐ์ฝ ์ ๊ฑฐ"""
return self._emoticons.sub('', text)
def clean(self, text,
remove_html=True,
remove_urls=True,
mask_personal=True,
normalize_ws=True,
normalize_unicode=True,
reduce_repeated=True):
"""์ ์ฒด ์ ์ ํ์ดํ๋ผ์ธ"""
if normalize_unicode:
text = self.normalize_unicode(text)
if remove_html:
text = self.remove_html(text)
if remove_urls:
text = self.remove_urls(text)
if mask_personal:
text = self.mask_personal_info(text)
if reduce_repeated:
text = self.reduce_repeated_chars(text)
if normalize_ws:
text = self.normalize_whitespace(text)
return text
# ์ฌ์ฉ ์์
cleaner = TextCleaner()
dirty_text = """
<h1>์๋
ํ์ธ์!!!</h1>
<p>์ ๋ ํ๊ธธ๋์
๋๋คใ
ใ
ใ
ใ
ใ
</p>
์ฐ๋ฝ์ฒ: 010-1234-5678
์ด๋ฉ์ผ: hong@example.com
์ฐธ๊ณ : https://www.example.com/page
"""
clean_text = cleaner.clean(dirty_text)
print(clean_text)
# "์๋
ํ์ธ์!! ์ ๋ ํ๊ธธ๋์
๋๋คใ
ใ
์ฐ๋ฝ์ฒ: [์ ํ๋ฒํธ] ์ด๋ฉ์ผ: [์ด๋ฉ์ผ] ์ฐธ๊ณ : [URL]"
๊ฐ์ธ์ ๋ณด ๋ง์คํน, HTML ์ธ์ ์ ๋ฐฉ์ง, ํ ์คํธ ์ ๊ทํ ๋ฑ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ ์ ์๊ฑฐ๋ !
โก 8. ์ฑ๋ฅ ์ต์ ํ โ ๋น ๋ฅธ ํ ์คํธ ์ฒ๋ฆฌ์ ๋น๊ฒฐ
๋์ฉ๋ ํ
์คํธ๋ฅผ ์ฒ๋ฆฌํ ๋๋ ์ฑ๋ฅ์ด ์ ๋ง ์ค์ํด!
๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋๋ผ๋ ๋ฐฉ๋ฒ์ ๋ฐ๋ผ ์๋ ์ฐจ์ด๊ฐ ์์ญ ๋ฐฐ๊น์ง ๋ ์ ์๊ฑฐ๋ . ๐๏ธ
import re
import time
# ์ฑ๋ฅ ํ
์คํธ ์์
# ๋์ ์: ๋ฃจํ ์์์ ๋งค๋ฒ ํจํด ์ปดํ์ผ
def bad_approach(texts):
results = []
for text in texts:
result = re.sub(r'\s+', ' ', text) # ๋งค๋ฒ ์ปดํ์ผ!
results.append(result)
return results
# ์ข์ ์: ๋ฏธ๋ฆฌ ์ปดํ์ผ
whitespace_pattern = re.compile(r'\s+')
def good_approach(texts):
return [whitespace_pattern.sub(' ', text) for text in texts]
# ๋ ์ข์ ์: ๋จ์ ์นํ์ str.replace()๊ฐ ๋ ๋น ๋ฅผ ์ ์์
def best_approach_simple(texts):
return [' '.join(text.split()) for text in texts]
# ๋ฌธ์์ด ๊ฒฐํฉ - ์ ๋ + ์ฐ์ฐ์๋ก ๋ฃจํ ๋๋ฆฌ์ง ๋ง!
# ๋์ ์
def bad_concat(words):
result = ""
for word in words:
result += word + " " # O(nยฒ) ๋ณต์ก๋!
return result.strip()
# ์ข์ ์
def good_concat(words):
return " ".join(words) # O(n) ๋ณต์ก๋!
# ์ฑ๋ฅ ์ฐจ์ด ํ์ธ
words = ["Python"] * 10000
start = time.time()
bad_concat(words)
print(f"+ ์ฐ์ฐ์: {time.time() - start:.4f}์ด")
start = time.time()
good_concat(words)
print(f"join(): {time.time() - start:.4f}์ด")
# join()์ด ์์ญ ๋ฐฐ ๋น ๋ฆ!
๐ฏ ์ฑ๋ฅ ์ต์ ํ ํต์ฌ ์์น
1๏ธโฃ ํจํด์ ๋ฏธ๋ฆฌ ์ปดํ์ผํ๋ผ
๊ฐ์ ํจํด์ ์ฌ๋ฌ ๋ฒ ์ด๋ค๋ฉด re.compile()๋ก ๋ฏธ๋ฆฌ ์ปดํ์ผํด๋์.
2๏ธโฃ ๋จ์ ์์
์ ๋ด์ฅ ๋ฉ์๋๋ฅผ ์จ๋ผ
๊ณ ์ ๋ฌธ์์ด ์นํ์ str.replace()๊ฐ ์ ๊ทํํ์๋ณด๋ค ๋น ๋ฅผ ์ ์์ด.
๋ฌธ์ ๋จ์ ๋ณํ์ str.translate()๊ฐ ์ต๊ฐ์ด์ผ.
3๏ธโฃ ๋ฌธ์์ด ๊ฒฐํฉ์ join()์ ์จ๋ผ
+ ์ฐ์ฐ์๋ก ๋ฃจํ์์ ๋ฌธ์์ด์ ์ด์ด๋ถ์ด๋ฉด O(nยฒ) ๋ณต์ก๋๊ฐ ๋ผ. ํญ์ ''.join()์ ์จ!
4๏ธโฃ ํ์์ ๋งค์นญ์ ์กฐ์ฌํด๋ผ
.* ๊ฐ์ ํ์์ ํจํด์ ๋ฐฑํธ๋ํน์ด ๋ง์ด ๋ฐ์ํ ์ ์์ด. ํ์ํ๋ฉด ๋นํ์์ (.*?)์ผ๋ก ๋ฐ๊ฟ๋ด.
5๏ธโฃ finditer()๋ฅผ ํ์ฉํด๋ผ
๋์ฉ๋ ํ
์คํธ์์ ๋ชจ๋ ๋งค์นญ์ ์ฒ๋ฆฌํ ๋ findall()๋ณด๋ค finditer()๊ฐ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ด์ผ.
๐ 9. ๊ณ ๊ธ ๊ธฐ๋ฒ โ ์๋ฉด ๋ ๋ฒจ์ ๋๋ ๊ฒ๋ค
๐ ์กฐ๊ฑด๋ถ ํจํด (Conditional Patterns)
import re
# (?(id)yes|no) - ๊ทธ๋ฃน id๊ฐ ๋งค์นญ๋์ผ๋ฉด yes ํจํด, ์๋๋ฉด no ํจํด
# ์: ๋ฐ์ดํ๊ฐ ์์ผ๋ฉด ๋ฐ์ดํ๋ก ๋๋์ผ ํ๊ณ , ์์ผ๋ฉด ๊ทธ๋ฅ ๋จ์ด
pattern = re.compile(r'("?)(\w+)(?(1)"|)')
# ๊ทธ๋ฃน 1("?)์ด ๋งค์นญ๋์ผ๋ฉด(๋ฐ์ดํ ์์ผ๋ฉด) ๋์ "๊ฐ ์์ผ ํจ
test_cases = ['"hello"', 'world', '"python']
for test in test_cases:
match = pattern.fullmatch(test)
print(f"'{test}': {'๋งค์นญ' if match else '๋ถ์ผ์น'}")
# '"hello"': ๋งค์นญ
# 'world': ๋งค์นญ
# '"python': ๋ถ์ผ์น โ ์์ ๋ฐ์ดํ๋ ์๋๋ฐ ๋ ๋ฐ์ดํ๊ฐ ์์!
๐ ์์์ ๊ทธ๋ฃน๊ณผ ์์ ์ ์๋์
import re
# Python์ re ๋ชจ๋์ ์์์ ๊ทธ๋ฃน์ ์ง์ ์ง์ํ์ง ์์ง๋ง
# regex ๋ชจ๋(pip install regex)์ ์ฌ์ฉํ๋ฉด ๊ฐ๋ฅํด
# pip install regex
import regex # ์ค์น ํ์
# ์์ ์ ์๋์ (Possessive Quantifier) - ๋ฐฑํธ๋ํน ์์
# ์ผ๋ฐ: r'\d+\b' vs ์์ ์ : r'\d++\b'
text = "12345abc"
# ์ผ๋ฐ ์๋์ - ๋ฐฑํธ๋ํน ๋ฐ์
result1 = re.search(r'\d+\b', text)
print(result1) # None (๋ฐฑํธ๋ํน์ผ๋ก ์๋ํ์ง๋ง ์คํจ)
# ์์์ ๊ทธ๋ฃน - ๋ฐฑํธ๋ํน ์์ (regex ๋ชจ๋)
result2 = regex.search(r'(?>\d+)\b', text)
print(result2) # None (๋ ๋น ๋ฅด๊ฒ ์คํจ)
๐ ์ ๊ทํํ์ ๋๋ฒ๊น โ regex101 ์คํ์ผ
import re
def debug_regex(pattern, text, flags=0):
"""์ ๊ทํํ์ ๋๋ฒ๊น
๋์ฐ๋ฏธ"""
try:
compiled = re.compile(pattern, flags)
matches = list(compiled.finditer(text))
print(f"ํจํด: {pattern}")
print(f"ํ
์คํธ: {text}")
print(f"๋งค์นญ ์: {len(matches)}๊ฐ")
print("-" * 50)
for i, match in enumerate(matches, 1):
print(f"๋งค์นญ {i}:")
print(f" ์ ์ฒด: '{match.group()}'")
print(f" ์์น: {match.span()}")
if match.lastindex:
for j in range(1, match.lastindex + 1):
print(f" ๊ทธ๋ฃน {j}: '{match.group(j)}'")
if match.groupdict():
for name, value in match.groupdict().items():
print(f" {name}: '{value}'")
# ๋งค์นญ ์๊ฐํ
print("\n์๊ฐํ:")
result = list(text)
for match in reversed(matches):
start, end = match.span()
result.insert(end, 'ใ')
result.insert(start, 'ใ')
print(''.join(result))
except re.error as e:
print(f"ํจํด ์ค๋ฅ: {e}")
# ์ฌ์ฉ ์์
debug_regex(
r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
"์ค๋์ 2024-01-15์ด๊ณ ๋ด์ผ์ 2024-01-16์
๋๋ค."
)
๐ ์ ๋์ฝ๋์ ๋ค๊ตญ์ด ์ฒ๋ฆฌ
import re
import unicodedata
# ์ ๋์ฝ๋ ์นดํ
๊ณ ๋ฆฌ๋ฅผ ์ด์ฉํ ๋ฌธ์ ๋ถ๋ฅ
def categorize_chars(text):
categories = {}
for char in text:
cat = unicodedata.category(char)
cat_name = {
'Lu': '๋๋ฌธ์', 'Ll': '์๋ฌธ์', 'Nd': '์ซ์',
'Zs': '๊ณต๋ฐฑ', 'Po': '๊ตฌ๋์ ', 'Lo': '๊ธฐํ๋ฌธ์(ํ๊ธ๋ฑ)'
}.get(cat, cat)
categories[cat_name] = categories.get(cat_name, 0) + 1
return categories
text = "Hello ์๋
123 !@#"
print(categorize_chars(text))
# ์ ๋์ฝ๋ ์์ฑ์ ์ด์ฉํ ํจํด (regex ๋ชจ๋)
# import regex
# ํ๊ธ, ์ผ๋ณธ์ด, ์ค๊ตญ์ด ๋ฑ ๋ชจ๋ ์์์ ๋ฌธ์
# pattern = regex.compile(r'\p{Han}+') # ํ์
# pattern = regex.compile(r'\p{Hangul}+') # ํ๊ธ
# pattern = regex.compile(r'\p{Hiragana}+') # ํ๋ผ๊ฐ๋
# ํ์ค re ๋ชจ๋๋ก ๋ค๊ตญ์ด ์ฒ๋ฆฌ
# ํ๊ธ: [๊ฐ-ํฃ] ๋๋ [\uAC00-\uD7A3]
# ์ผ๋ณธ์ด ํ๋ผ๊ฐ๋: [\u3040-\u309F]
# ์ผ๋ณธ์ด ๊ฐํ์นด๋: [\u30A0-\u30FF]
# ํ์: [\u4E00-\u9FFF]
multilingual = "Hello ์๋
ใใใซใกใฏ ไฝ ๅฅฝ ู
ุฑุญุจุง"
korean = re.findall(r'[๊ฐ-ํฃ]+', multilingual)
japanese = re.findall(r'[\u3040-\u30FF]+', multilingual)
chinese = re.findall(r'[\u4E00-\u9FFF]+', multilingual)
print(f"ํ๊ตญ์ด: {korean}") # ['์๋
']
print(f"์ผ๋ณธ์ด: {japanese}") # ['ใใใซใกใฏ']
print(f"์ค๊ตญ์ด: {chinese}") # ['ไฝ ๅฅฝ']
# ์ ๋์ฝ๋ ์ ๊ทํ
# NFC: ์กฐํฉํ (ํ๊ธ ์์ฑํ)
# NFD: ๋ถํดํ (ํ๊ธ ์๋ชจ ๋ถ๋ฆฌ)
# NFKC: ํธํ์ฑ ์กฐํฉํ (์ ๊ฐโ๋ฐ๊ฐ ๋ฑ)
# NFKD: ํธํ์ฑ ๋ถํดํ
text_with_fullwidth = "๏ผก๏ผข๏ผฃ๏ผค๏ผ๏ผ๏ผ๏ผ"
normalized = unicodedata.normalize('NFKC', text_with_fullwidth)
print(normalized) # "ABCD1234" (์ ๊ฐโ๋ฐ๊ฐ ๋ณํ)
๐ 10. ์์ฃผ ํ๋ ์ค์์ ์ฃผ์์ฌํญ
์ ๊ทํํ์์ ์ฐ๋ค ๋ณด๋ฉด ๋๊ตฌ๋ ํ ๋ฒ์ฉ ๋น ์ง๋ ํจ์ ๋ค์ด ์์ด.
๋ฏธ๋ฆฌ ์์๋๋ฉด ์ฝ์ง์ ์ค์ผ ์ ์์ด! ๐
import re
html = "<b>์ฒซ ๋ฒ์งธ</b> ํ
์คํธ <b>๋ ๋ฒ์งธ</b>"
# ์๋ชป๋ ๋ฐฉ๋ฒ - ํ์์ ๋งค์นญ
wrong = re.findall(r'<b>.+</b>', html)
print(wrong) # ['<b>์ฒซ ๋ฒ์งธ</b> ํ
์คํธ <b>๋ ๋ฒ์งธ</b>'] โ ์ ์ฒด๋ฅผ ํ๋๋ก!
# ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ - ๋นํ์์ ๋งค์นญ
correct = re.findall(r'<b>.+?</b>', html)
print(correct) # ['<b>์ฒซ ๋ฒ์งธ</b>', '<b>๋ ๋ฒ์งธ</b>'] โ ๊ฐ๊ฐ ๋ฐ๋ก!
import re
# ์๋ชป๋ ๋ฐฉ๋ฒ - raw string ๋ฏธ์ฌ์ฉ
# \d๋ ํ์ด์ฌ ๋ฌธ์์ด์์ ๊ทธ๋ฅ d๋ก ์ฒ๋ฆฌ๋ ์ ์์
try:
result = re.findall('\d+', "abc123") # ์ด๊ฑด ์ฐ์ฐํ ์๋ํจ
result2 = re.findall('\b\w+\b', "hello world") # \b๊ฐ ๋ฐฑ์คํ์ด์ค๋ก!
print(result2) # ์์๊ณผ ๋ค๋ฅธ ๊ฒฐ๊ณผ
except:
pass
# ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ - raw string ์ฌ์ฉ
result = re.findall(r'\d+', "abc123") # ['123']
result2 = re.findall(r'\b\w+\b', "hello world") # ['hello', 'world']
print(result2)
import re
text = "์ค๋์ 2024๋
1์ 15์ผ์
๋๋ค"
# match๋ ์ฒ์๋ถํฐ ๋งค์นญ - ์ซ์๋ก ์์ํ์ง ์์ผ๋ None!
result_match = re.match(r'\d+', text)
print(result_match) # None
# search๋ ์ ์ฒด ํ์ - ์ค๊ฐ์ ์ซ์๋ ์ฐพ์
result_search = re.search(r'\d+', text)
print(result_search.group()) # '2024'
# ์ ์ฒด ๋ฌธ์์ด์ด ํจํด๊ณผ ์ผ์นํ๋์ง ํ์ธํ๋ ค๋ฉด fullmatch() ์ฌ์ฉ
print(re.fullmatch(r'\d{4}', "2024")) # ๋งค์นญ
print(re.fullmatch(r'\d{4}', "20245")) # None (๊ธธ์ด ๋ค๋ฆ)
import re
# ๋๋ฌด ๋จ์ํ ์ด๋ฉ์ผ ํจํด - ๋ง์ ์ ํจํ ์ด๋ฉ์ผ์ ๋์นจ
simple_pattern = r'\w+@\w+\.\w+'
# test+tag@example.com โ + ๊ธฐํธ ๋๋ฌธ์ ์คํจ!
# user@sub.domain.com โ ์๋ธ๋๋ฉ์ธ ๋๋ฌธ์ ์คํจ!
# ๋ ๋์ ํจํด
better_pattern = r'[\w.+-]+@[\w-]+(?:\.[\w-]+)*\.\w{2,}'
test_emails = [
"user@example.com",
"test+tag@example.com",
"user@sub.domain.co.kr",
"user.name@example.org"
]
for email in test_emails:
simple = bool(re.match(r'^\w+@\w+\.\w+$', email))
better = bool(re.match(r'^[\w.+-]+@[\w-]+(?:\.[\w-]+)*\.\w{2,}$', email))
print(f"{email}: ๋จ์={simple}, ๊ฐ์ ={better}")
์ค๋ฌด์์๋ ์ ๋นํ ๊ด๋ํ ํจํด์ผ๋ก ๊ธฐ๋ณธ ํ์๋ง ํ์ธํ๊ณ , ์ค์ ์ ํจ์ฑ์ ์ด๋ฉ์ผ ๋ฐ์ก ํ ํ์ธํ๋ ๋ฐฉ์์ ๋ง์ด ์จ!
# ์ ๊ทํํ์์ด ํญ์ ์ต์ ์ ์๋์ผ!
# ๋์ ์: ๋จ์ ํฌํจ ์ฌ๋ถ ํ์ธ์ ์ ๊ทํํ์ ์ฌ์ฉ
import re
text = "Hello World"
if re.search(r'World', text): # ์ค๋ฒํฌ!
print("์์")
# ์ข์ ์: ๋จ์ ํฌํจ ์ฌ๋ถ๋ in ์ฐ์ฐ์
if 'World' in text:
print("์์")
# ๋์ ์: ๋จ์ ์นํ์ ์ ๊ทํํ์ ์ฌ์ฉ
result = re.sub(r'Hello', 'Hi', text) # ์ค๋ฒํฌ!
# ์ข์ ์: ๋จ์ ์นํ์ str.replace()
result = text.replace('Hello', 'Hi')
# ์ ๊ทํํ์์ด ํ์ํ ๊ฒฝ์ฐ:
# - ํจํด์ด ๋ณต์กํ ๋ (์ด๋ฉ์ผ, URL, ๋ ์ง ํ์ ๋ฑ)
# - ์ฌ๋ฌ ํจํด์ ๋์์ ์ฒ๋ฆฌํ ๋
# - ๊ทธ๋ฃน ์บก์ฒ๊ฐ ํ์ํ ๋
# - ๋์ ํจํด์ด ํ์ํ ๋
๐ 11. ๋ง๋ฌด๋ฆฌ โ ์ ๊ทํํ์ ๋ง์คํฐ๊ฐ ๋๋ ๊ธธ
์, ์ฌ๊ธฐ๊น์ง ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐
์ ๊ทํํ์๊ณผ ๊ณ ๊ธ ๋ฌธ์์ด ์กฐ์์ ์ฒ์์ ์ด๋ ต๊ฒ ๋๊ปด์ง์ง๋ง, ํ ๋ฒ ์ตํ๋ฉด ์ฝ๋ฉ ์์ฐ์ฑ์ด ๋ช ๋ฐฐ๋ก ์ฌ๋ผ๊ฐ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ผ.
์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด๋ฉด:
โ
re ๋ชจ๋์ ํต์ฌ ํจ์๋ค โ match, search, findall, finditer, sub, split
โ
ํจํด ๋ฌธ๋ฒ โ ๋ฉํ๋ฌธ์, ์๋์, ์ต์ปค, ๊ทธ๋ฃน, ๋ฃฉ์ดํค๋/๋ฃฉ๋นํ์ธ๋
โ
์ฑ๋ฅ ์ต์ ํ โ compile(), ๋ด์ฅ ๋ฉ์๋ ํ์ฉ, join() ์ฌ์ฉ
โ
์ค์ ํจํด โ ์ด๋ฉ์ผ, ์ ํ๋ฒํธ, URL, ๋ ์ง, ํ๊ธ ์ถ์ถ
โ
๊ณ ๊ธ ๊ธฐ๋ฒ โ ์ ๋์ฝ๋ ์ฒ๋ฆฌ, ํ
์คํธ ์ ์ ํ์ดํ๋ผ์ธ
โ
์์ฃผ ํ๋ ์ค์ โ ํ์์ ๋งค์นญ, ๋ฐฑ์ฌ๋์, match vs search ํผ๋
๐ค๏ธ ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๋ ค๋ฉด
regex ๋ชจ๋ ํ๊ตฌ โ ํ์ค re ๋ชจ๋๋ณด๋ค ๊ฐ๋ ฅํ ๊ธฐ๋ฅ๋ค (์์์ ๊ทธ๋ฃน, ์์ ์ ์๋์, ์ ๋์ฝ๋ ์์ฑ ๋ฑ)
ํ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ต โ HTML/XML์ ์ ๊ทํํ์๋ณด๋ค BeautifulSoup, lxml ๊ฐ์ ํ์๊ฐ ๋ ์ ํฉํด!
NLP ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๊ตฌ โ ํ ์คํธ ์ฒ๋ฆฌ๋ฅผ ๋ ๊น์ด ํ๊ณ ์ถ๋ค๋ฉด NLTK, spaCy, KoNLPy(ํ๊ตญ์ด) ๋ฅผ ๊ณต๋ถํด๋ด!
์ค์ ํ๋ก์ ํธ โ ๋ก๊ทธ ๋ถ์๊ธฐ, ๋ฐ์ดํฐ ์ ์ ํ์ดํ๋ผ์ธ, ํฌ๋กค๋ฌ ๋ฑ์ ์ง์ ๋ง๋ค์ด๋ด. ์ค์ ์ด ์ต๊ณ ์ ์ ์๋์ด์ผ!
regex101.com ํ์ฉ โ ์ ๊ทํํ์์ ์ค์๊ฐ์ผ๋ก ํ ์คํธํ๊ณ ๋๋ฒ๊น ํ ์ ์๋ ์ต๊ณ ์ ์จ๋ผ์ธ ๋๊ตฌ์ผ!
Python ๊ฐ๋ฐ ์ค๋ ฅ์ ํค์ฐ๊ณ ์ถ๋ค๋ฉด ์ฌ๋ฅ๋ท์์ Python ์ ๋ฌธ๊ฐ๋ค์ ๊ฐ์๋ ์ฝ๋ ๋ฆฌ๋ทฐ ์๋น์ค๋ฅผ ์ฐพ์๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ.
๋ค์ํ ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ค์ด ๋ชจ์ฌ ์์ด์ ์ค๋ฌด ๋
ธํ์ฐ๋ฅผ ๋น ๋ฅด๊ฒ ์ต๋ํ ์ ์๊ฑฐ๋ ! ๐
์ ๊ทํํ์์ ์ฒ์์ ์ธ๊ณ์ด์ฒ๋ผ ๋ณด์ด์ง๋ง, ๊พธ์คํ ์ฐ๋ค ๋ณด๋ฉด ์ด๋ ์๊ฐ ์์ฐ์ค๋ฝ๊ฒ ์ฝํ๋ ๋ ์ด ์.
๊ทธ ๋ ์ด ์ค๋ฉด ๋๋ ์ง์ง ํ
์คํธ ์ฒ๋ฆฌ ๋ง์คํฐ๊ฐ ๋ ๊ฑฐ์ผ! ๐ช
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.