정규 표현식 추가문제 - 파일명 정렬 (Python)

 

코딩테스트 연습 - [3차] 파일명 정렬

알고리즘 문제 연습 카카오톡 친구해요! 프로그래머스 교육 카카오 채널을 만들었어요. 여기를 눌러, 친구 추가를 해주세요. 신규 교육 과정 소식은 물론 다양한 이벤트 소식을 가장 먼저 알려

school.programmers.co.kr

파일명 리스트를 HEAD, NUMBER, TAIL 세 부분으로 분리하고, 대소문자를 구분하지 않는 문자열 정렬과 숫자 크기 정렬을 조합하여 수행하는 문제입니다. 정규표현식의 그룹 캡처 기능을 활용하면 복잡한 분기문 없이도 데이터를 정밀하게 분리할 수 있습니다.


1.  나의 풀이

정규표현식을 미리 컴파일하여 성능을 최적화하고, re.match를 통해 문자열의 시작부터 패턴을 분석하여 데이터를 추출했습니다.

import re

def solution(files):
    files_for_sort = []
    # 숫자가 아닌 문자열(HEAD)과 1~5글자의 숫자(NUMBER)를 그룹으로 지정합니다.
    pattern = re.compile(r'(\D+)(\d{1,5})')
    
    for file in files:
        # 문자열의 시작부터 패턴과 일치하는지 확인합니다.
        group = re.match(pattern, file)
        
        # 각 그룹 데이터를 추출하여 정렬용 리스트에 저장합니다.
        head = group[1]
        number = group[2]
        
        # 정렬 기준인 HEAD(소문자화)와 NUMBER(정수화)를 원본과 함께 보관합니다.
        files_for_sort.append([file, head.lower(), int(number)])

    # 다중 조건 정렬: 1순위 HEAD(문자열), 2순위 NUMBER(수치)
    # 파이썬의 sort는 안정 정렬이므로 나머지 TAIL 순서는 원본을 유지합니다.
    files_for_sort.sort(key=lambda x: (x[1], x[2]))
    
    answer = []
    for file in files_for_sort:
        answer.append(file[0])
        
    return answer

2. 오늘 배운 점 및 복기 노트

정규표현식 컴파일(re.compile)의 목적

동일한 정규표현식 패턴을 반복문 내에서 여러 번 사용해야 할 때, re.compile을 사용하여 패턴 객체를 미리 만들어두면 효율적입니다. 파이썬은 패턴을 내부적으로 해석하고 최적화하는 과정을 한 번만 수행한 뒤 재사용하므로, 매번 함수에 문자열 패턴을 넘기는 것보다 실행 속도 면에서 유리합니다. 이는 특히 대량의 파일명을 처리해야 하는 코딩 테스트 환경에서 유의미한 성능 차이를 만듭니다.

패턴 (\D+)(\d{1,5})에 대한 상세 해석

\D+는 숫자가 아닌 모든 문자(Non-digit)가 1개 이상 연속됨을 의미하며 이는 파일명의 HEAD 부분을 담당합니다. \d{1,5}는 숫자(Digit)가 최소 1개에서 최대 5개까지 연속됨을 의미하며 이는 NUMBER 부분을 담당합니다. 괄호 ()는 캡처 그룹(Capturing Group)을 의미하며, 전체 문자열에서 해당 조건에 맞는 부분만 메모리에 따로 저장하여 추출할 수 있게 해줍니다.

re.match와 그룹 인덱스의 이해

re.match는 문자열의 시작 부분이 패턴과 일치하는지 검사합니다. 만약 파일명이 패턴의 첫 번째 조건인 비숫자(\D+)로 시작하지 않거나, 이어서 숫자(\d{1,5})가 나오지 않는다면 None을 반환합니다. 즉, 전체 패턴이 모두 충족되어야만 매치 객체가 생성되는 'All or Nothing' 원칙을 따릅니다.

매칭 성공 시 인덱스를 통해 그룹에 접근할 수 있습니다.

  • group[0]: 매칭된 전체 문자열 (HEAD + NUMBER)
  • group[1]: 첫 번째 괄호 내용 (HEAD)
  • group[2]: 두 번째 괄호 내용 (NUMBER)

다중 조건 정렬과 람다(Lambda) 활용법

sort 함수의 key 인자에 람다식을 사용하면 여러 정렬 기준을 우선순위에 따라 설정할 수 있습니다. lambda x: (x[1], x[2])와 같이 튜플 형태로 기준을 넘기면 파이썬은 x[1](HEAD)로 먼저 정렬을 수행한 뒤, 이 값이 같을 경우에만 x[2](NUMBER)로 2차 정렬을 수행합니다.

안정 정렬(Stable Sort)의 특성 활용

파이썬의 정렬 알고리즘(Timsort)은 값이 같은 요소들의 상대적인 순서를 보장하는 안정 정렬입니다. 이 문제에서는 HEAD와 NUMBER가 모두 같을 경우 원본의 순서를 유지해야 한다는 조건이 있습니다. 정렬 키에 TAIL이나 원본 인덱스를 포함하지 않더라도, 파이썬의 기본 정렬 특성 덕분에 입력 순서가 자연스럽게 보존됩니다.

수치 비교를 위한 int() 형변환

문자열 상태에서 "09"와 "2"를 비교하면 사전순에 따라 "09"가 앞에 오게 됩니다. 하지만 문제 요구사항은 수치상의 크기 비교이므로 int()를 통해 정수로 변환하여 비교해야 합니다. int() 변환을 사용하면 "012"와 "12"가 동일한 수치인 12로 취급되므로, 앞서 언급한 안정 정렬 특성과 결합하여 문제의 복잡한 조건을 깔끔하게 해결할 수 있습니다.