Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬에서 숫자가 포함된 문자열을 올바르게 정렬하는 방법 (자연 정렬)

문자열 안에 포함된 숫자를 기준으로 정렬하고 싶을 때 사용하는 방식을 자연 정렬(Natural Sort) 또는 휴먼 정렬(Human Sort)이라고 부릅니다.

자연 정렬이 필요한 이유

예를 들어 다음과 같은 리스트가 있다고 가정해 보겠습니다.

['Hello1', 'Hello12', 'Hello29', 'Hello2', 'Hello17', 'Hello25']

사람이 기대하는 정렬 결과는 아래와 같습니다.

['Hello1', 'Hello2', 'Hello12', 'Hello17', 'Hello25', 'Hello29']

하지만 파이썬의 기본 문자열 정렬은 사전순(ASCII 순서)으로 비교하기 때문에 다음과 같은 결과가 나옵니다.

['Hello1', 'Hello12', 'Hello17', 'Hello2', 'Hello25', 'Hello29']

'Hello12'가 'Hello2'보다 앞에 오는 것은 사람이 생각하는 숫자 크기 순서와 다르기 때문에, 단순히 sort()만으로는 원하는 결과를 얻을 수 없습니다.

해결 방법: sort()의 key 매개변수 활용

이 문제는 sort() 함수가 제공하는 key 매개변수를 사용하면 해결할 수 있습니다. key는 리스트의 각 항목에서 비교 기준값을 계산하는 함수입니다.

여기서는 정규표현식(regular expression)을 이용해 문자열에서 숫자 부분을 추출하고, 텍스트와 숫자를 함께 고려하여 정렬합니다.

예제 코드

import re

def atoi(text):
    # 숫자로만 이루어진 부분은 int로 변환, 그 외에는 그대로 반환
    return int(text) if text.isdigit() else text

def natural_keys(text):
    # 문자열을 숫자와 비숫자로 분리한 뒤 각 조각을 변환
    return [atoi(c) for c in re.split(r'(\d+)', text)]

my_list = ['Hello1', 'Hello12', 'Hello29', 'Hello2', 'Hello17', 'Hello25']

my_list.sort(key=natural_keys)
print(my_list)

실행 결과

['Hello1', 'Hello2', 'Hello12', 'Hello17', 'Hello25', 'Hello29']

동작 원리 살펴보기

re.split(r'(\d+)', text)는 캡처 그룹을 사용하므로, 문자열을 숫자 부분과 텍스트 부분으로 나눌 때 구분자까지 결과에 포함됩니다. 예를 들어 'Hello25'['Hello', '25', '']로 분리되고, 각 요소가 atoi()를 통해 ['Hello', 25, '']로 변환됩니다.

파이썬은 리스트끼리 비교할 때 요소를 하나씩 순서대로 비교하므로, 먼저 'Hello'라는 공통 접두사가 같은지 확인한 뒤 숫자 값인 25와 2 등을 실제 숫자로 비교하게 됩니다. 덕분에 'Hello2'가 'Hello12'보다 작다고 판단하여 사람이 기대하는 순서대로 정렬이 이루어집니다.

마무리

파일명 정렬, 버전 번호 정렬처럼 문자열 속 숫자가 중요한 경우에는 자연 정렬이 필수적입니다. 위의 natural_keys 패턴은 재사용성이 높으니 유틸리티 함수로 만들어 두면 다양한 상황에서 유용하게 활용할 수 있습니다.