Python/Chapter10. 정규표현식, 딕셔너리+, 튜플

lullaby ·2025년 10월 1일

Python

목록 보기
10/13
post-thumbnail

1. 정규표현식(Regular Expressions) 핵심 개념~~시험범위 미포함

1.1 정규표현식 기본 개념

  • 정의: 특정한 규칙을 가진 문자열의 집합을 표현하는 형식 언어
  • 용도: 문자열 검색, 치환, 필터링 등에 사용
  • 파이썬 모듈: re 모듈 사용 (파이썬 기본 라이브러리)

1.2 메타 문자

다음 문자들은 특별한 의미를 가진 메타 문자입니다:

. ^ $ * + ? { } [ ] \ | ( )

주요 메타 문자 의미:

  • [ ] - 문자 클래스: 괄호 안의 문자 중 하나와 매치
    • [abc]: a, b, c 중 하나와 매치
    • [a-z]: a부터 z까지 소문자 알파벳과 매치
    • [0-9]: 숫자와 매치
    • [^0-9]: 숫자가 아닌 문자와 매치 (^ 은 부정)
  1. 단축 표현:
    • \d: 숫자와 매치 (= [0-9])
    • \D: 숫자가 아닌 것과 매치 (= [^0-9])
    • \s: 공백 문자와 매치 (= [ \t\n\r\f\v])
    • \S: 공백이 아닌 문자와 매치
    • \w: 문자+숫자와 매치 (= [a-zA-Z0-9_])
    • \W: 문자+숫자가 아닌 것과 매치
  2. . (Dot): 줄바꿈(\n)을 제외한 모든 문자와 매치
    • a.b: a와 b 사이에 어떤 문자든 한 개 있는 패턴
  3. 반복 관련 메타 문자:
    • : 0번 이상 반복 (= {0,})
    • +: 1번 이상 반복 (= {1,})
    • ?: 0번 또는 1번 (= {0,1})
    • {m}: 정확히 m번 반복
    • {m,n}: m번 이상 n번 이하 반복

1.3 정규표현식 사용 방법

  1. re.compile(): 정규표현식 패턴 컴파일

    import re
    p = re.compile('[a-z]+')  # 소문자 알파벳 1개 이상
    
  2. 주요 메서드:

    • match(): 문자열의 처음부터 매치 검사
    • search(): 문자열 전체에서 첫 번째 매치 검사
    • findall(): 모든 매치를 리스트로 반환
    • finditer(): 모든 매치를 반복 가능한 객체로 반환
  3. 매치 객체 메서드:

    • group(): 매치된 문자열 반환
    • start(): 매치 시작 위치 반환
    • end(): 매치 끝 위치 반환
    • span(): (시작, 끝) 튜플 반환

2. 딕셔너리(Dictionary)와 튜플(Tuple)

2.1 딕셔너리(Dictionary)

  • 정의: 키(key)와 값(value)의 쌍으로 이루어진 자료구조
  • 특징:
    • 키는 중복될 수 없음
    • 키를 통해 값에 빠르게 접근 가능
    • 순서가 없음 (Python 3.7부터는 삽입 순서 유지)

딕셔너리 활용 예:

# 단어 카운팅
word_count = {}
for word in words:
    if word not in word_count:
        word_count[word] = 1
    else:
        word_count[word] += 1

2.2 튜플(Tuple)

  • 정의: 순서가 있는 변경 불가능한(immutable) 자료구조
  • 특징:
    • 한번 생성하면 변경할 수 없음
    • 인덱싱, 슬라이싱 가능
    • 여러 값을 패킹/언패킹하여 동시에 다룰 수 있음

튜플 활용 예:

# 함수에서 여러 값 반환
def calculate_circle(r):
    area = math.pi * r * r
    circumference = 2 * math.pi * r
    return (area, circumference)

# 언패킹
(a, c) = calculate_circle(10)

예상문제 및 풀이

1. 정규표현식 (Regular Expressions) 문제

문제 1: 기본 개념

다음 정규표현식이 매치하는 패턴을 설명하세요.

  1. [a-zA-Z0-9]+
  2. \d{3}-\d{3}-\d{4}
  3. [^0-9]
  4. a.b
  5. ca{2,5}t

답안:

  1. 알파벳 대소문자와 숫자가 1개 이상 반복되는 패턴
  2. 000-000-0000 형식의 전화번호 패턴
  3. 숫자가 아닌 모든 문자 중 1개
  4. a와 b 사이에 어떤 문자든 한 개 있는 패턴(예: aab, a0b)
  5. c 다음에 a가 2~5회 반복된 후 t가 오는 패턴(caat, caaat, caaaat, caaaaat)

문제 2: re 모듈 메서드

다음 코드의 실행 결과를 예측하세요.

import re

text = "Python is fun. python is easy. PYTHON is powerful."
pattern = re.compile('[Pp]ython')

# (a) match 메서드 결과
result1 = pattern.match(text)
print(result1)

# (b) search 메서드 결과
result2 = pattern.search(text)
print(result2)

# (c) findall 메서드 결과
result3 = pattern.findall(text)
print(result3)

답안:

# (a) match 메서드 결과
<re.Match object; span=(0, 6), match='Python'>

# (b) search 메서드 결과
<re.Match object; span=(0, 6), match='Python'>

# (c) findall 메서드 결과
['Python', 'python']

문제 3: 이메일 주소 검증

다음 이메일 주소가 올바른 형식인지 검증하는 정규표현식 패턴을 작성하세요.
이메일 형식: 사용자이름@도메인.최상위도메인

import re

def validate_email(email):
    pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
    if pattern.match(email):
        return True
    else:
        return False

# 테스트
emails = ["user@example.com", "invalid-email", "another.user@sub.domain.co.kr", "no@domain"]
for email in emails:
    print(f"{email}: {validate_email(email)}")

실행 결과:

user@example.com: True
invalid-email: False
another.user@sub.domain.co.kr: True
no@domain: False

문제 4: 로그 파일 분석

다음 로그 파일에서 에러 코드가 포함된 라인만 추출하는 프로그램을 작성하세요.
에러 코드 형식: ERROR-숫자4자리

import re

def extract_errors(log_file):
    error_pattern = re.compile(r'ERROR-\d{4}')
    errors = []

    with open(log_file, 'r') as f:
        for line in f:
            if error_pattern.search(line):
                errors.append(line.strip())

    return errors

# 예시 로그 파일 생성 (실제 시험에서는 주어질 수 있음)
sample_log = """
2023-05-14 12:30:45 INFO: System started
2023-05-14 12:35:22 ERROR-1033: Invalid argument
2023-05-14 12:40:11 WARNING: Low memory
2023-05-14 12:45:30 ERROR-2045: Connection failed
2023-05-14 12:50:15 INFO: Process completed
"""

with open('sample.log', 'w') as f:
    f.write(sample_log)

# 에러 라인 추출
errors = extract_errors('sample.log')
for error in errors:
    print(error)

실행 결과:

2023-05-14 12:35:22 ERROR-1033: Invalid argument
2023-05-14 12:45:30 ERROR-2045: Connection failed

2. 딕셔너리와 튜플 문제

문제 5: 단어 빈도수 계산

텍스트 파일에서 각 단어의 빈도수를 계산하여 출력하는 프로그램을 작성하세요.

def count_words(filename):
    word_count = {}

    try:
        with open(filename, 'r') as file:
            for line in file:
                words = line.strip().split()
                for word in words:
                    if word not in word_count:
                        word_count[word] = 1
                    else:
                        word_count[word] += 1

        return word_count
    except FileNotFoundError:
        print(f"파일 '{filename}'을 찾을 수 없습니다.")
        return {}

# 테스트용 파일 생성
sample_text = """
Well begun is half done.
Good morning
Birds of a feather flock together.
Well begun is half done.
Birds of a feather flock together.
Well begun is half done.
"""

with open('word_count.txt', 'w') as f:
    f.write(sample_text)

# 단어 빈도수 계산
result = count_words('word_count.txt')
print(result)

실행 결과:

{'Well': 3, 'begun': 3, 'is': 3, 'half': 3, 'done.': 3, 'Good': 1, 'morning': 1, 'Birds': 2, 'of': 2, 'a': 2, 'feather': 2, 'flock': 2, 'together.': 2}

문제 6: 축약어 변환기

축약어를 원래 단어로 변환하는 프로그램을 작성하세요.

def expand_abbreviations(message):
    abbreviations = {
        "B4": "Before",
        "TX": "Thanks",
        "BBL": "Be Back Later",
        "BCNU": "Be Seeing You",
        "HAND": "Have A Nice Day"
    }

    words = message.split()
    result = ""

    for word in words:
        if word in abbreviations:
            result += abbreviations[word] + " "
        else:
            result += word + " "

    return result.strip()

# 테스트
message = "TX Mr. Park! HAND"
expanded = expand_abbreviations(message)
print(f"입력: {message}")
print(f"출력: {expanded}")

실행 결과:

입력: TX Mr. Park! HAND
출력: Thanks Mr. Park! Have A Nice Day

문제 7: 원의 계산 함수

반지름을 입력받아 원의 넓이와 둘레를 계산하여 튜플로 반환하는 함수를 작성하세요.

import math

def calculate_circle(radius):
    area = math.pi * radius ** 2
    circumference = 2 * math.pi * radius
    return (area, circumference)

# 테스트
radius = 10
area, circumference = calculate_circle(radius)
print(f"반지름이 {radius}인 원의:")
print(f"- 넓이: {area}")
print(f"- 둘레: {circumference}")

실행 결과:

반지름이 10인 원의:
- 넓이: 314.1592653589793
- 둘레: 62.83185307179586

문제 8: 학생 정보 관리

학생 정보를 관리하는 프로그램을 작성하세요. 각 학생은 (이름, 나이, 전공) 튜플로 저장되며,
학생 목록을 딕셔너리로 관리하세요. 학번(키)으로 학생 정보를 조회하는 기능을 구현하세요.

def add_student(students, student_id, name, age, major):
    students[student_id] = (name, age, major)
    return students

def get_student(students, student_id):
    if student_id in students:
        return students[student_id]
    else:
        return None

def print_student_info(student):
    if student:
        name, age, major = student
        print(f"이름: {name}")
        print(f"나이: {age}")
        print(f"전공: {major}")
    else:
        print("학생 정보가 없습니다.")

# 테스트
students = {}
students = add_student(students, "2023001", "홍길동", 20, "컴퓨터과학")
students = add_student(students, "2023002", "김철수", 19, "수학")
students = add_student(students, "2023003", "이영희", 21, "물리학")

print("전체 학생 목록:")
for student_id, info in students.items():
    print(f"{student_id}: {info[0]}")

print("\n학번 2023002 학생 정보:")
student = get_student(students, "2023002")
print_student_info(student)

print("\n학번 2023004 학생 정보:")
student = get_student(students, "2023004")
print_student_info(student)

실행 결과:

전체 학생 목록:
2023001: 홍길동
2023002: 김철수
2023003: 이영희

학번 2023002 학생 정보:
이름: 김철수
나이: 19
전공: 수학

학번 2023004 학생 정보:
학생 정보가 없습니다.

3. 종합 문제

문제 9: 전화번호부 관리

정규표현식을 사용하여 전화번호 형식을 검증하고, 딕셔너리를 사용하여 전화번호부를 관리하는 프로그램을 작성하세요.

import re

class PhoneBook:
    def __init__(self):
        self.contacts = {}
        # 전화번호 형식: 000-000-0000 또는 (000) 000-0000
        self.phone_pattern = re.compile(r'^\(\d{3}\)\s\d{3}-\d{4}$|^\d{3}-\d{3}-\d{4}$')

    def add_contact(self, name, phone_number):
        if not self.is_valid_phone(phone_number):
            print(f"유효하지 않은 전화번호 형식입니다: {phone_number}")
            return False

        self.contacts[name] = phone_number
        print(f"{name} 연락처가 추가되었습니다.")
        return True

    def is_valid_phone(self, phone_number):
        return bool(self.phone_pattern.match(phone_number))

    def find_contact(self, name):
        if name in self.contacts:
            return self.contacts[name]
        else:
            return None

    def list_contacts(self):
        if not self.contacts:
            print("전화번호부가 비어있습니다.")
        else:
            print("연락처 목록:")
            for name, phone in self.contacts.items():
                print(f"{name}: {phone}")

# 테스트
phone_book = PhoneBook()
phone_book.add_contact("홍길동", "010-123-4567")  # 잘못된 형식
phone_book.add_contact("홍길동", "010-1234-5678")  # 잘못된 형식
phone_book.add_contact("홍길동", "010-123-4567")  # 잘못된 형식
phone_book.add_contact("홍길동", "123-456-7890")  # 올바른 형식
phone_book.add_contact("김철수", "(123) 456-7890")  # 올바른 형식
phone_book.add_contact("이영희", "987-654-3210")  # 올바른 형식

print("\n홍길동의 연락처:", phone_book.find_contact("홍길동"))
print("박지성의 연락처:", phone_book.find_contact("박지성"))

print("\n전체 연락처 목록:")
phone_book.list_contacts()

실행 결과:

유효하지 않은 전화번호 형식입니다: 010-123-4567
유효하지 않은 전화번호 형식입니다: 010-1234-5678
유효하지 않은 전화번호 형식입니다: 010-123-4567
홍길동 연락처가 추가되었습니다.
김철수 연락처가 추가되었습니다.
이영희 연락처가 추가되었습니다.

홍길동의 연락처: 123-456-7890
박지성의 연락처: None

전체 연락처 목록:
연락처 목록:
홍길동: 123-456-7890
김철수: (123) 456-7890
이영희: 987-654-3210

문제 10: 이메일 주소 추출 및 통계

주어진 텍스트에서 모든 이메일 주소를 추출하고, 도메인별 이메일 개수를 계산하는 프로그램을 작성하세요.

import re

def extract_emails(text):
    email_pattern = re.compile(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}')
    return email_pattern.findall(text)

def count_domains(emails):
    domain_counts = {}

    for email in emails:
        # @ 이후의 도메인 부분 추출
        domain = email.split('@')[1]

        if domain not in domain_counts:
            domain_counts[domain] = 1
        else:
            domain_counts[domain] += 1

    return domain_counts

# 테스트용 텍스트
sample_text = """
Contact us at support@example.com for help.
Send your resume to jobs@company.co.kr if you're interested.
For technical questions: tech@support.example.com or admin@example.com.
Marketing team: marketing@company.co.kr, sales@company.co.kr.
"""

# 이메일 추출
emails = extract_emails(sample_text)
print("추출된 이메일 주소:")
for email in emails:
    print(email)

# 도메인별 통계
domain_stats = count_domains(emails)
print("\n도메인별 이메일 개수:")
for domain, count in domain_stats.items():
    print(f"{domain}: {count}개")

실행 결과:

추출된 이메일 주소:
support@example.com
jobs@company.co.kr
tech@support.example.com
admin@example.com
marketing@company.co.kr
sales@company.co.kr

도메인별 이메일 개수:
example.com: 2개
company.co.kr: 3개
support.example.com: 1개

0개의 댓글