파이썬 - 정규 표현식

안녕하세요, 미래의 파이썬 마법사 여러분! 오늘은 파이썬의 정규 표현식(regex)의 세계로 흥미진진한 여정을 떠나보겠습니다. 정규 표현식에 대해 들어본 적이 없다고 해도 걱정 마세요 - 이 튜토리얼 끝에는 이 강력한 도구를 프로처럼 사용할 수 있게 될 거예요!

Python - Reg Expressions

정규 표현식이란?

빠져들기 전에 정규 표현식이란 무엇인지 이해해보죠. 텍스트의 바다에서 특정 패턴을 찾는 탐정이라고 상상해보세요. 정규 표현식은 마그니팅 글라스처럼, 문자열을 패턴에 따라 검색하고 조작하는 데 도움을 주는 것입니다. 멋지지 않나요?

라우 문자열

파이썬에서 정규 표현식을 사용할 때는 종종 라우 문자열을 사용합니다. 이 문자열은 'r'로 시작하며, 백슬래시를 리터럴 문자로 취급합니다. 정규 표현식에서는 백슬래시가 자주 사용되기 때문에 매우 유용합니다.

# 일반 문자열
print("Hello\nWorld")
# 라우 문자열
print(r"Hello\nWorld")

첫 번째 경우에는 "Hello"와 "World"가 별도의 줄에 출력됩니다. 두 번째 경우에는 "Hello\nWorld"가 그대로 출력됩니다. 이는 정규 표현식 패턴 작업에서 중요합니다.

메타 문자

메타 문자는 정규 표현식의 빌딩 블록입니다. 특별한 의미를 가지고 있으며, 패턴을 정의하는 데 도움을 줍니다. 몇 가지 일반적인 메타 문자를 살펴보죠:

메타 문자 의미
. 줄바꿈을 제외한 모든 문자를 일치시킵니다
^ 문자열의 시작을 일치시킵니다
$ 문자열의 끝을 일치시킵니다
* 0개 이상의 반복을 일치시킵니다
+ 1개 이상의 반복을 일치시킵니다
? 0개 또는 1개의 반복을 일치시킵니다
{} 명시적으로 지정된 수의 반복을 일치시킵니다
[] 일치할 문자 집합을 지정합니다
\ 특수 문자를 이스케이프 합니다

re.match() 함수

re.match() 함수는 문자열의 시작 부분에서 패턴과 일치 시도합니다. 일치하는 것을 찾으면 일치 객체를 반환하고, 그렇지 않으면 None을 반환합니다.

import re

result = re.match(r"Hello", "Hello, World!")
if result:
print("일치 발견:", result.group())
else:
print("일치 없음")

이 코드는 "일치 발견: Hello"를 출력합니다. group() 메서드는 일치된 서브 문자열을 반환합니다.

re.search() 함수

re.match()는 문자열의 시작 부분에서 일치를 찾지만, re.search()는 전체 문자열에서 일치를 찾습니다.

import re

result = re.search(r"World", "Hello, World!")
if result:
print("일치 발견:", result.group())
else:
print("일치 없음")

이 코드는 "일치 발견: World"를 출력합니다.

일치 vs 검색

match()search()의 주요 차이점은 match()가 문자열의 시작 부분에서만 일치를 확인하는 반면, search()는 문자열의 어디서나 일치를 확인합니다.

re.findall() 함수

re.findall() 함수는 문자열에서 패턴과 일치하는 모든 비중복 일치를 리스트로 반환합니다.

import re

text = "The rain in Spain falls mainly in the plain"
result = re.findall(r"ain", text)
print(result)

이 코드는 ['ain', 'ain', 'ain']를 출력합니다.

re.sub() 함수

re.sub() 함수는 문자열에서 패턴과 일치하는 모든 발생을 치환 문자열로 대체합니다.

import re

text = "The rain in Spain"
result = re.sub(r"a", "o", text)
print(result)

이 코드는 "The roin in Spoin"를 출력합니다.

re.compile() 함수

re.compile() 함수는 재사용하기 위해 정규 표현식 객체를 만들어줍니다. 동일한 패턴을 여러 번 사용할 때는 더 효율적일 수 있습니다.

import re

pattern = re.compile(r"\d+")
result1 = pattern.findall("There are 123 apples and 456 oranges")
result2 = pattern.findall("I have 789 bananas")

print(result1)
print(result2)

이 코드는 ['123', '456']['789']를 출력합니다.

re.finditer() 함수

re.finditer() 함수는 문자열에서 패턴과 일치하는 모든 비중복 일치에 대한 일치 객체를 생성하는 이터레이터를 반환합니다.

import re

text = "The rain in Spain"
for match in re.finditer(r"ain", text):
print(f"Found '{match.group()}' at position {match.start()}-{match.end()}")

이 코드는 다음과 같이 출력합니다:

Found 'ain' at position 5-8
Found 'ain' at position 17-20

파이썬 정규 표현식의 사용 사례

정규 표현식은 많은 실용적인 응용이 있습니다. 일반적인 사용 사례를 살펴보죠:

모음으로 시작하는 단어 찾기

import re

text = "An apple a day keeps the doctor away"
vowel_words = re.findall(r'\b[aeiouAEIOU]\w+', text)
print(vowel_words)

이 코드는 ['An', 'apple', 'a', 'away']를 출력합니다.

정규 표현식 수정자: 옵션 플래그

파이썬의 re 모듈은 패턴 해석 방식을 수정하는 여러 옵션 플래그를 제공합니다:

플래그 설명
re.IGNORECASE (re.I) 대소문자를 구분하지 않는 일치를 수행합니다
re.MULTILINE (re.M) ^가 각 줄의 시작과, $가 각 줄의 끝을 일치시킵니다
re.DOTALL (re.S) .가 줄바꿈을 포함한 모든 문자를 일치시킵니다
re.VERBOSE (re.X) 더 가독성 있는 정규 표현식 패턴을 작성할 수 있게 합니다

정규 표현식 패턴

더 복잡한 패턴을 탐구해보죠:

문자 클래스

문자 클래스는 일치할 문자 집합을 지정할 수 있게 합니다:

import re

text = "The quick brown fox jumps over the lazy dog"
result = re.findall(r"[aeiou]", text)
print(result)

이 코드는 텍스트에서 모든 모음을 찾아 출력합니다.

특수 문자 클래스

파이썬 정규 표현식은 특수 문자 클래스를 지원합니다:

클래스 설명
\d 모든 십진수를 일치시킵니다
\D 모든 비숫자 문자를 일치시킵니다
\s 모든 공백 문자를 일치시킵니다
\S 모든 비공백 문자를 일치시킵니다
\w 모든 알파벳 또는 숫자 문자를 일치시킵니다
\W 모든 비알파벳 또는 비숫자 문자를 일치시킵니다

반복 사례

패턴이 몇 번 반복되어야 하는지를 지정할 수 있습니다:

import re

text = "I have 111 apples and 22 oranges"
result = re.findall(r"\d{2,3}", text)
print(result)

이 코드는 ['111', '22']를 출력합니다, 2개 또는 3개의 숫자로 구성된 번호를 일치시킵니다.

비탐욕적 반복

기본적으로 반복은 탐욕적이며, 가능한 한 많이 일치합니다. 반복 뒤에 ?를 추가하면 비탐욕적으로 만듭니다:

import re

text = "<h1>Title</h1><p>Paragraph</p>"
greedy = re.findall(r"<.*>", text)
non_greedy = re.findall(r"<.*?>", text)
print("Greedy:", greedy)
print("Non-greedy:", non_greedy)

이 코드는 탐욕적과 비탐욕적 일치의 차이를 보여줍니다.

괄호를 사용한 그룹화

괄호를 사용하면 정규 표현식의 부분을 그룹화할 수 있습니다:

import re

text = "John Smith ([email protected])"
result = re.search(r"(\w+) (\w+) \((\w+@\w+\.\w+)\)", text)
if result:
print(f"Full Name: {result.group(1)} {result.group(2)}")
print(f"Email: {result.group(3)}")

이 코드는 텍스트에서 이름과 이메일을 추출합니다.

역참조

역참조는 이전에 일치한 그룹을 참조할 수 있게 합니다:

import re

text = "<h1>Title</h1><p>Paragraph</p>"
result = re.findall(r"<(\w+)>.*?</\1>", text)
print(result)

이 코드는 시작과 종료 태그가 일치하는 HTML 태그를 일치시킵니다.

대안

| 문자는 대안을 지정할 수 있습니다:

import re

text = "The color of the sky is blue or gray"
result = re.search(r"blue|gray", text)
if result:
print(f"Found color: {result.group()}")

이 코드는 "blue" 또는 "gray" 중 하나를 일치시킵니다.

앵커

앵커는 텍스트에서 위치를 지정합니다:

import re

text = "Python is awesome"
start = re.match(r"^Python", text)
end = re.search(r"awesome$", text)
print(f"Starts with Python: {bool(start)}")
print(f"Ends with awesome: {bool(end)}")

이 코드는 텍스트가 "Python"으로 시작하고 "awesome"으로 끝나는지 확인합니다.

괄호를 사용한 특수 문법

괄호는 그룹화에 불과하게 사용됩니다:

  • (?:...)는 캡처하지 않는 그룹을 만듭니다
  • (?P...)는 이름付き 그룹을 만듭니다
  • (?=...)는 긍정적 전망을 만듭니다
  • (?!...)는 부정적 전망을 만듭니다
import re

text = "Python version 3.9.5"
result = re.search(r"Python (?:version )?(?P<version>\d+\.\d+\.\d+)", text)
if result:
print(f"Version: {result.group('version')}")

이 코드는 버전 번호를 추출합니다, "version"이 텍스트에 있는지 여부에 관계없이.

그리고 여기까지! 우리는 파이썬 정규 표현식의 세계를 거쳐 기본부터 상급 개념까지 여정을 떠났습니다. 마치 어떤 강력한 도구를 마스터하기 위해서는 연습이 필요하듯이, 정규 표현식도 처음에는 어려울 수 있습니다. 하지만 계속 실험하다 보면, 패턴을 찾는 데 능숙해질 거예요! 코딩하세용!

Credits: Image by storyset