KR_RegularExpression - somaz94/python-study GitHub Wiki
์ ๊ทํํ์์ ๊ธฐ๋ณธ ํจํด๋ค์ด๋ค.
import re
# ๊ธฐ๋ณธ ๋งค์นญ
text = "Hello, World!"
match = re.search(r"World", text)
print(match.group()) # World
# ๋ฉํ๋ฌธ์ ์ฌ์ฉ
pattern = r"\d+" # ํ๋ ์ด์์ ์ซ์
text = "There are 123 apples"
match = re.search(pattern, text)
print(match.group()) # 123
# ๋ฌธ์์ด ์์๊ณผ ๋ ๋งค์นญ
text = "Python is amazing"
start_match = re.search(r"^Python", text) # ๋ฌธ์์ด ์์์ด "Python"์ธ์ง ํ์ธ
print(start_match.group() if start_match else "No match") # Python
end_match = re.search(r"amazing$", text) # ๋ฌธ์์ด ๋์ด "amazing"์ธ์ง ํ์ธ
print(end_match.group() if end_match else "No match") # amazing
# ๋์๋ฌธ์ ๊ตฌ๋ถ ์๋ ๋งค์นญ
case_insensitive = re.search(r"python", text, re.IGNORECASE)
print(case_insensitive.group() if case_insensitive else "No match") # Python
# ๋ณต์ ์ต์
์ ์ฉ
multiline_text = """First line
Second line
Third line"""
multiline_match = re.findall(r"^.*line", multiline_text, re.MULTILINE)
print(multiline_match) # ['First line', 'Second line', 'Third line']
โ
ํน์ง:
- ๊ธฐ๋ณธ ๋ฌธ์์ด ๋งค์นญ ๋ฐ ํจํด ๊ฒ์
- ๋ค์ํ ๋ฉํ๋ฌธ์๋ฅผ ์ด์ฉํ ํจํด ์ ์
- ๋ฌธ์์ด ์์น ๊ธฐ๋ฐ ๋งค์นญ (์์, ๋)
- ์ ๊ทํํ์ ํ๋๊ทธ ํ์ฉ (๋์๋ฌธ์ ๋ฌด์, ๋ฉํฐ๋ผ์ธ ๋ฑ)
- ๋งค์นญ ๊ฒฐ๊ณผ ์ถ์ถ ๋ฐ ์ฒ๋ฆฌ
- ๋ค์ํ ์ํฉ์ ๋ง๋ ํจํด ์ ์ฉ
- ๋ค์ํ ์ธ์ด์์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋๋ ํ์ค
- ํ ์คํธ ์ฒ๋ฆฌ์ ๊ฐ๋ ฅํ ๋๊ตฌ
์ ๊ทํํ์์์ ๊ฐ์ฅ ํํ๊ฒ ์ฌ์ฉ๋๋ ๋ฉํ๋ฌธ์์ ํจํด๋ค์ด๋ค.
import re
# ๊ธฐ๋ณธ ๋ฉํ๋ฌธ์
# \d: ์ซ์ [0-9]
# \D: ์ซ์๊ฐ ์๋ ๋ฌธ์ [^0-9]
# \w: ๋จ์ด ๋ฌธ์ (์ํ๋ฒณ, ์ซ์, ์ธ๋์ค์ฝ์ด) [a-zA-Z0-9_]
# \W: ๋จ์ด ๋ฌธ์๊ฐ ์๋ ๊ฒ [^a-zA-Z0-9_]
# \s: ๊ณต๋ฐฑ ๋ฌธ์ (์คํ์ด์ค, ํญ, ์ค๋ฐ๊ฟ ๋ฑ)
# \S: ๊ณต๋ฐฑ์ด ์๋ ๋ฌธ์
# .: ์์์ ํ ๋ฌธ์ (์ค๋ฐ๊ฟ ์ ์ธ)
text = "abc123 def456 ghi789"
# ๋ชจ๋ ์ซ์ ์ฐพ๊ธฐ
numbers = re.findall(r"\d+", text)
print(numbers) # ['123', '456', '789']
# ๋ชจ๋ ๋ฌธ์ ๊ทธ๋ฃน ์ฐพ๊ธฐ
words = re.findall(r"\w+", text)
print(words) # ['abc123', 'def456', 'ghi789']
# ๋ฌธ์์ ์ซ์ ๋ถ๋ฆฌํ๊ธฐ
alphanumeric = re.findall(r"([a-z]+)(\d+)", text)
print(alphanumeric) # [('abc', '123'), ('def', '456'), ('ghi', '789')]
# ๋ฌธ์ ํด๋์ค
# [...]: ๊ดํธ ์์ ์ด๋ค ๋ฌธ์๋ ๋งค์นญ
# [^...]: ๊ดํธ ์์ ๋ฌธ์๋ค์ ์ ์ธํ ๋งค์นญ
# [a-z]: ์ํ๋ฒณ ์๋ฌธ์ ๋ฒ์
# [A-Z]: ์ํ๋ฒณ ๋๋ฌธ์ ๋ฒ์
# [0-9]: ์ซ์ ๋ฒ์
# [๊ฐ-ํฃ]: ํ๊ธ ๋ฒ์
# ๋ชจ์๋ง ์ฐพ๊ธฐ
vowels = re.findall(r"[aeiou]", "Hello World")
print(vowels) # ['e', 'o', 'o']
# ์ซ์์ ํน์๋ฌธ์๋ง ์ฐพ๊ธฐ
symbols = re.findall(r"[0-9!@#$%^&*]", "password123!")
print(symbols) # ['1', '2', '3', '!']
# ํ๊ธ ์ฐพ๊ธฐ
korean = re.findall(r"[๊ฐ-ํฃ]+", "Hello ์๋
ํ์ธ์ World")
print(korean) # ['์๋
ํ์ธ์']
# ์ต์ปค
# ^: ๋ฌธ์์ด์ ์์
# $: ๋ฌธ์์ด์ ๋
# \b: ๋จ์ด ๊ฒฝ๊ณ
# \B: ๋จ์ด ๊ฒฝ๊ณ๊ฐ ์๋ ์์น
# ๋จ์ด ๊ฒฝ๊ณ ํ์ฉ
word_boundary = re.findall(r"\bcat\b", "The cat sat on the cats")
print(word_boundary) # ['cat']
# ์ด๋ฉ์ผ ์ฃผ์ ๊ฒ์ฆ
def is_valid_email(email):
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
return bool(re.match(pattern, email))
print(is_valid_email("[email protected]")) # True
print(is_valid_email("invalid_email")) # False
print(is_valid_email("user@domain")) # False
# URL ์ถ์ถ
text_with_urls = "Visit our website at https://www.example.com or http://subdomain.example.org"
urls = re.findall(r"https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?:/\S*)?", text_with_urls)
print(urls) # ['https://www.example.com', 'http://subdomain.example.org']
โ
ํน์ง:
- ๋ค์ํ ๋ฌธ์ ํด๋์ค๋ฅผ ํ์ฉํ ํจํด ์ ์
- ํน์ ๋ฌธ์ ์งํฉ์ด๋ ๋ฒ์๋ฅผ ์ง์ ํ๋ ๋ฐฉ๋ฒ
- ๋ถ์ ํจํด์ ์ฌ์ฉํ ์ ์ธ ๋งค์นญ
- ๋จ์ด ๊ฒฝ๊ณ์ ์์น ์ต์ปค๋ฅผ ํ์ฉํ ์ ํํ ๋งค์นญ
- ์ค์ฉ์ ์ธ ์ ๊ทํํ์ ํจํด ์์ (์ด๋ฉ์ผ, URL ๋ฑ)
- ๋ณตํฉ์ ์ธ ํจํด ๊ตฌ์ฑ ๋ฐฉ๋ฒ
- ํจํด์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ ๊ณ ๋ ค
- ๋ค๊ตญ์ด ํ ์คํธ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ
์ ๊ทํํ์์์ ๋ฌธ์๋ ํจํด์ ๋ฐ๋ณต์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
import re
# ์ฃผ์ ๋ฐ๋ณต ์๋์
# *: 0ํ ์ด์ ๋ฐ๋ณต (0๊ฐ ์ด์)
# +: 1ํ ์ด์ ๋ฐ๋ณต (1๊ฐ ์ด์)
# ?: 0ํ ๋๋ 1ํ ๋ฐ๋ณต (์ ํ์ )
# {n}: ์ ํํ nํ ๋ฐ๋ณต
# {n,}: nํ ์ด์ ๋ฐ๋ณต
# {n,m}: nํ ์ด์ mํ ์ดํ ๋ฐ๋ณต
text = "hello 123 world 456"
# * ์๋์: 0ํ ์ด์
print(re.findall(r"he*llo", "hllo hello heello")) # ['hllo', 'hello', 'heello']
# + ์๋์: 1ํ ์ด์
print(re.findall(r"he+llo", "hllo hello heello")) # ['hello', 'heello']
# ? ์๋์: 0ํ ๋๋ 1ํ
print(re.findall(r"colou?r", "color colour")) # ['color', 'colour']
# {n} ์ ํํ ๋ฐ๋ณต ํ์
pattern = r"ca{2}t"
print(re.findall(pattern, "cat caat caaat")) # ['caat']
# {n,} ์ต์ ๋ฐ๋ณต ํ์
pattern = r"ca{2,}t"
print(re.findall(pattern, "cat caat caaat caaaat")) # ['caat', 'caaat', 'caaaat']
# {n,m} ๋ฐ๋ณต ๋ฒ์
pattern = r"ca{2,3}t"
print(re.findall(pattern, "cat caat caaat caaaat")) # ['caat', 'caaat']
# ํ์์ vs ๋นํ์์ ๋ฐ๋ณต
greedy_pattern = r"<.+>"
non_greedy_pattern = r"<.+?>"
html = "<p>์ฒซ ๋ฒ์งธ ๋จ๋ฝ</p><p>๋ ๋ฒ์งธ ๋จ๋ฝ</p>"
# ํ์์ ๋งค์นญ (๊ธฐ๋ณธ): ๊ฐ๋ฅํ ๋ง์ด ๋งค์นญ
print(re.findall(greedy_pattern, html)) # ['<p>์ฒซ ๋ฒ์งธ ๋จ๋ฝ</p><p>๋ ๋ฒ์งธ ๋จ๋ฝ</p>']
# ๋นํ์์ ๋งค์นญ (?): ๊ฐ๋ฅํ ์ ๊ฒ ๋งค์นญ
print(re.findall(non_greedy_pattern, html)) # ['<p>', '</p>', '<p>', '</p>']
# ๋ณต์กํ ๋ฐ๋ณต ํจํด
# ์ ํ๋ฒํธ ํ์ ๊ฒ์ฆ (์: 010-1234-5678 ๋๋ 010-123-4567)
phone_pattern = r"01[016789]-\d{3,4}-\d{4}"
phones = [
"010-1234-5678",
"011-123-4567",
"016-123-4567",
"010-12-345", # ์๋ชป๋ ํ์
"010-12345-6789" # ์๋ชป๋ ํ์
]
for phone in phones:
is_valid = re.match(phone_pattern, phone)
print(f"{phone}: {'์ ํจ' if is_valid else '์ ํจํ์ง ์์'}")
# ๋ฐ๋ณต ํจํด์ ํ์ฉํ ๋จ์ด ์ถ์ถ
sentence = "Python programming is fun and challenging. Python is powerful."
# Python ๋จ์ด๋ง ์ถ์ถ
print(re.findall(r"\bPython\b", sentence)) # ['Python', 'Python']
# ๋ฐ๋ณต ํจํด์ ๊ทธ๋ฃน ๊ฒฐํฉ
date_text = "Date: 2023-05-15, Modified: 2023-06-30"
dates = re.findall(r"(\d{4})-(\d{2})-(\d{2})", date_text)
print(dates) # [('2023', '05', '15'), ('2023', '06', '30')]
# ์ฐ์๋ ์ซ์ ๋ฐ ๋ฌธ์ ํจํด
text = "123abc456def789ghi"
# ์ซ์์ ๋ฌธ์ ํจํด์ด ๋ฐ๋ณต๋๋ ๊ฒฝ์ฐ
pattern = r"(\d+)([a-z]+)"
matches = re.findall(pattern, text)
print(matches) # [('123', 'abc'), ('456', 'def'), ('789', 'ghi')]
โ
ํน์ง:
- ๋ค์ํ ๋ฐ๋ณต ์๋์๋ฅผ ์ฌ์ฉํ ํจํด ๋งค์นญ
- ์ ํํ ๋ฐ๋ณต ํ์ ์ง์ ๋ฐ ๋ฒ์ ์ค์
- ํ์์ (greedy) ๋ฐ ๋นํ์์ (non-greedy) ๋งค์นญ ๋ฐฉ์
- ๋ณต์กํ ํจํด์์ ๋ฐ๋ณต ์ ์ด ๋ฐฉ๋ฒ
- ์ค์ ์์ฉ ์ฌ๋ก(์ ํ๋ฒํธ, ๋ ์ง ๋ฑ) ๊ฒ์ฆ
- ๋ฐ๋ณต ํจํด๊ณผ ๊ทธ๋ฃน์ ๊ฒฐํฉ
- ํจ์จ์ ์ธ ๋ฐ๋ณต ํจํด ์์ฑ๋ฒ
- ๋ค์ํ ํ์์ ํ ์คํธ ํจํด ๋งค์นญ
์ ๊ทํํ์์์ ํจํด์ ์ผ๋ถ๋ฅผ ๊ทธ๋ฃนํํ๊ณ ์ฐธ์กฐํ๋ ๋ฐฉ๋ฒ์ด๋ค.
import re
# ๊ธฐ๋ณธ ๊ทธ๋ฃนํ - ๊ดํธ() ์ฌ์ฉ
text = "Smith,John"
pattern = r"(\w+),(\w+)"
match = re.match(pattern, text)
if match:
# ๊ทธ๋ฃน ์ ์ฒด ๋งค์น
print(match.group(0)) # Smith,John
# ๊ฐ๋ณ ๊ทธ๋ฃน ์ ๊ทผ
print(match.group(1)) # Smith
print(match.group(2)) # John
# ๋ชจ๋ ๊ทธ๋ฃน ํ ๋ฒ์ ๊ฐ์ ธ์ค๊ธฐ
print(match.groups()) # ('Smith', 'John')
# ์ด๋ฆ ์๋ ๊ทธ๋ฃน (?P<name>pattern)
pattern = r"(?P<last>\w+),(?P<first>\w+)"
match = re.match(pattern, text)
if match:
# ์ด๋ฆ์ผ๋ก ๊ทธ๋ฃน ์ ๊ทผ
print(match.group('last')) # Smith
print(match.group('first')) # John
# groupdict() ๋ฉ์๋๋ก ์ฌ์ ํํ๋ก ๊ฐ์ ธ์ค๊ธฐ
print(match.groupdict()) # {'last': 'Smith', 'first': 'John'}
# ๊ทธ๋ฃน ๋ด ์ ํ ํจํด (|)
colors = "red green blue yellow"
# red ๋๋ green ๋๋ blue ๋งค์นญ
color_pattern = r"(red|green|blue)"
color_matches = re.findall(color_pattern, colors)
print(color_matches) # ['red', 'green', 'blue']
# ๋น์บก์ฒ ๊ทธ๋ฃน (?:pattern)
# ๋งค์นญ์ ํ์ง๋ง ๊ฒฐ๊ณผ์ ํฌํจํ์ง ์์
text = "apple and banana"
pattern = r"(?:apple|banana) and (\w+)"
match = re.search(pattern, text)
print(match.groups()) # ('banana',)
# ํ๋ฐฉ ์ฐธ์กฐ (๋ฐฑ๋ ํผ๋ฐ์ค)
# \๋ฒํธ ๋๋ \g<๋ฒํธ>๋ก ์ด์ ๊ทธ๋ฃน ์ฐธ์กฐ
html = "<div>๋ด์ฉ1</div><span>๋ด์ฉ2</span>"
# ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ: ํ๊ทธ ์ด๋ฆ์ด ์ผ์นํ์ง ์์๋ ๋งค์นญ๋จ
print(re.findall(r"<(\w+)>(.*?)</(\w+)>", html))
# [('div', '๋ด์ฉ1', 'div'), ('span', '๋ด์ฉ2', 'span')]
# ํ๋ฐฉ ์ฐธ์กฐ: ์ฌ๋ ํ๊ทธ์ ๋ซ๋ ํ๊ทธ๊ฐ ์ผ์นํด์ผ ํจ
print(re.findall(r"<(\w+)>(.*?)</\1>", html))
# [('div', '๋ด์ฉ1'), ('span', '๋ด์ฉ2')]
# ์ด๋ฆ ์๋ ๊ทธ๋ฃน ์ฐธ์กฐ (?P=name)
pattern = r"<(?P<tag>\w+)>(.*?)</(?P=tag)>"
matches = re.findall(pattern, html)
print(matches) # [('div', '๋ด์ฉ1'), ('span', '๋ด์ฉ2')]
# ๋ฌธ์์ด ์นํ์์ ๊ทธ๋ฃน ์ฐธ์กฐ
text = "John Smith"
# ์ด๋ฆ ํ์ ๋ฐ๊พธ๊ธฐ (First Last -> Last, First)
new_text = re.sub(r"(\w+) (\w+)", r"\2, \1", text)
print(new_text) # Smith, John
# ์ค์ฒฉ ๊ทธ๋ฃน
address = "123 Main St, Anytown, CA 12345"
pattern = r"((\d+) ([A-Za-z\s]+), ([A-Za-z]+), ([A-Z]{2}) (\d{5}))"
match = re.search(pattern, address)
if match:
# ์ ์ฒด ์ฃผ์
print(match.group(1)) # 123 Main St, Anytown, CA 12345
# ๋ฒ์ง์
print(match.group(2)) # 123
# ๋๋ก๋ช
print(match.group(3)) # Main St
# ๋์
print(match.group(4)) # Anytown
# ์ฃผ
print(match.group(5)) # CA
# ์ฐํธ๋ฒํธ
print(match.group(6)) # 12345
# lookahead์ lookbehind ์ด์ค์
# ๊ธ์ ํ lookahead (?=pattern): pattern์ด ๋ค์ ์์ด์ผ ๋งค์นญ
# ๋ถ์ ํ lookahead (?!pattern): pattern์ด ๋ค์ ์์ด์ผ ๋งค์นญ
# ๊ธ์ ํ lookbehind (?<=pattern): pattern์ด ์์ ์์ด์ผ ๋งค์นญ
# ๋ถ์ ํ lookbehind (?<!pattern): pattern์ด ์์ ์์ด์ผ ๋งค์นญ
# ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ (์๋ฌธ, ์ซ์, ํน์๋ฌธ์ ์กฐํฉ 8์ ์ด์)
passwords = ["abc123", "Password1", "P@ssw0rd", "Simple"]
for pwd in passwords:
# ๊ธ์ ํ lookahead ์ฌ์ฉ
has_length = re.search(r".{8,}", pwd) is not None
has_upper = re.search(r"(?=.*[A-Z])", pwd) is not None
has_digit = re.search(r"(?=.*\d)", pwd) is not None
has_special = re.search(r"(?=.*[!@#$%^&*])", pwd) is not None
is_valid = has_length and has_upper and has_digit and has_special
print(f"{pwd}: {'์ ํจ' if is_valid else '์ ํจํ์ง ์์'}")
# ๊ฒฐ๊ณผ: 'P@ssw0rd'๋ง ์ ํจ
# ๊ธ์ ํ lookbehind: $ ๋ค์ ์ซ์ ์ถ์ถ
price_text = "Items: $10, $20, $30"
prices = re.findall(r"(?<=\$)\d+", price_text)
print(prices) # ['10', '20', '30']
# ๋ถ์ ํ lookahead: ํน์ ํจํด์ผ๋ก ๋๋์ง ์๋ ๋จ์ด
text = "apple, applet, application, apply"
not_ending_with_y = re.findall(r"\b\w+(?!y\b)", text)
print(not_ending_with_y) # ['apple', 'applet', 'application', 'appl']
โ
ํน์ง:
- ๊ดํธ๋ฅผ ์ฌ์ฉํ ํจํด ์ผ๋ถ ๊ทธ๋ฃนํ
- ์ธ๋ฑ์ค ๋๋ ์ด๋ฆ์ผ๋ก ๊ทธ๋ฃน ์ ๊ทผ
- ๋น์บก์ฒ ๊ทธ๋ฃน์ผ๋ก ๋งค์นญ์ ํ๋ ๊ฒฐ๊ณผ์์ ์ ์ธ
- ํ๋ฐฉ ์ฐธ์กฐ๋ก ๋์ผ ํจํด ์ฌ์ฌ์ฉ
- ์ค์ฒฉ ๊ทธ๋ฃน์ผ๋ก ๋ณต์กํ ํจํด ๊ตฌ์กฐํ
- ์ ๊ท์ ์นํ์์ ๊ทธ๋ฃน ์ฐธ์กฐ ํ์ฉ
- lookahead์ lookbehind ์ด์ค์ ์ผ๋ก ์กฐ๊ฑด๋ถ ๋งค์นญ
- ๋ณต์กํ ํ ์คํธ ๊ฒ์ฆ ๋ฐ ์ถ์ถ ํจํด ๊ตฌํ