본문 바로가기

프로그래밍/Python

한글을 포함한 문자열을 너비를 지정하여 포매팅할 때의 문제

728x90
>>> l = [ '김철수', 'Meggy', '이용' ]
>>> names = [ '김철수', 'Meggy', '이용' ]
>>> nums = [ 33, 23, 145 ]
>>> for name, num in zip(names, nums):
    print("%-10s %10s %10s"%(name, num, name))


김철수                33        김철수
Meggy              23      Meggy
이용                145         이용

한글이 포함된 경우에는 너비를 지정하는 문자열 포매팅이 잘 맞지 않는다. 이는 한글, 일본어 가나, 중국어 한자 등의 폭이 영문문자의 2배라는 점을 기본 문자열 포매팅이 고려하지 않고, 한글자는 무조건 한칸으로 생각하기 때문에 발생하는 문제이다. (영문 한칸의 너비만 차지하는 반각문자도 있지만, 간단한 설명을 위해 넘어갑시다.)

어떤 문제가 발생하는지 다시 코드를 보면, "%10s" 등으로 각 컬럼이 10칸을 차지하도록 코딩하여, 세 라인의 데이터가 테이블 형태로 잘 정렬되어서 나왔으면 하지만, 중간에 한글이 끼어들어가면서, 수직이 비뚤어지게 출력된다.

보통 터미널 등 cli 환경에서 한글을 포함한 출력을 표/테이블 형태로 예쁘게 맞추려 할 때 이런 문제에 맞딱뜨리게 된다.

이런 현상을 수정하기 위해서는 문자열이 실제로 몇칸을 차지하는지 폭을 계산해서 처리해야 하는데, 그걸 해주는 패키지가 wcwidth 이다. wcwidth 를 사용하여, 폭과 좌우중간정렬을 인자로 받아 포매팅해 주는 함수를 만들어, 다음과 같이 의도했던 테이블 형태의 출력을 할 수 있다.

from wcwidth import wcswidth 


def fmt(x, w, align='r'):
    """
        동아시아문자 폭을 고려하여, 문자열 포매팅을 해 주는 함수.
        w 는 해당 문자열과 스페이스문자가 차지하는 너비.
        align 은 문자열의 수평방향 정렬 좌/우/중간.
    """
    x = str(x)
    l = wcswidth(x)
    s = w-l
    if s <= 0:
        return x
    if align == 'l':
        return x + ' '*s
    if align == 'c':
        sl = s//2
        sr = s - sl
        return ' '*sl + x + ' '*sr
    return ' '*s + x


l = [ '김철수', '이열', 'Meg']
l2 = [ 33, 23, 145 ]

for a, b in zip(l, l2):
    #print('%-10s %10s %10s'%(a, b, a))
    print('%s %s %s | %s |'%(fmt(a, 10, 'l'), fmt(b, 10), fmt(a, 10), fmt(a, 10, 'c')))

결과 ( 본 블로그의 html 스타일 등 때문에 약간 비뚤어져 보일 수 있으나, 터미널 등의 환경에서는 잘 정렬되어 나옴. )

김철수             33     김철수 |   김철수   |
이열               23       이열 |    이열    |
Meg               145        Meg |    Meg     |