[Python 2] 숫자를 한글로 읽은 표현을 출력하는 코드

in #kr-newbie7 years ago (edited)

21616135_127282371332339_8565160149791795308_n.jpg
안녕하세요. NbelowP입니다. 스팀잇에서의 첫 포스팅은 프로그래밍으로 하게 되었습니다.
숫자를 입력하면 한글로 읽은 표현을 출력하는 코드를 작성해보기로 했습니다.
단, 전부 붙여서 읽을 경우 가독성이 떨어지므로 네 자리마다 띄어서 읽기로 했습니다.
예를 들어, 1234567과 같은 경우에 백이십삼만 사천오백육십칠과 같은 식으로 읽는 것을 말합니다.
또한, 0이 들어오면 을 출력해야 하며, 100과 같은 경우 일백이 아닌 으로 읽어야 합니다.
만일 숫자 앞에 불필요한 0이 붙어 있으면 무시합니다. 예를 들어, 00100으로 읽으면 됩니다.


의사 코드 작성

문제 해결을 위해 필요한 함수의 의사 코드를 먼저 작성해 보도록 하겠습니다.
기본적으로, 한글의 숫자 읽기는 네 자리씩 끊어서 읽게 됩니다.
따라서, 네 자리까지는 따로 처리하고, 그보다 많으면 네 자리씩 끊어서 읽어주면 됩니다.
이를 의사 코드로 작성하면 다음과 같습니다.

만일 네 자리 이하의 숫자가 입력될 경우
    네 자리를 한글로 적당히 읽어서 리턴한다.
이외의 경우
    뒤에서부터 네 자리씩 숫자를 끊는다.
    각각 끊은 부분에 대해 재귀 호출한다.
    이후 사이에 알맞은 단위를 넣는다.
    사이에 공백을 두고 연결한다.
    최종 결과를 리턴한다.

이제 네 자리 이하의 숫자를 읽는 방법에 대해서 생각해 봐야 합니다.
한 자리, 두 자리, 세 자리, 네 자리로 나눠 각각을 처리해 줄 수도 있지만, 재귀를 이용하도록 하겠습니다.
한 자리 숫자의 경우는 따로 처리하겠습니다. 만일 한 자리 숫자가 0인 경우, 읽지 않고 넘어가서 나중에 따로 처리하도록 하겠습니다. 이것은 재귀를 이용하기 위해서입니다. 0을 읽게 된다면 재귀를 이용하였을 때, 20이십영으로 읽는 것과 같은 상황이 나오게 됩니다.
한 자리 숫자가 아닌 경우는 가장 앞자리 숫자를 읽고 0이 아닐 경우 맞는 단위를 추가하면 됩니다. 가장 앞자리 숫자가 0인 경우는 제외한 나머지 부분을 재귀 호출하면 됩니다.
이를 의사 코드로 작성하면 다음과 같습니다.

    만일 숫자가 한 자리일 경우
        만일 숫자가 0인 경우
            빈 문자열을 리턴한다.
        이외의 경우
            한 자리 숫자를 읽어서 리턴한다.
    이외의 경우
        가장 앞자리 숫자를 읽는다.
        만일 가장 앞자리 숫자가 0인 경우
            앞자리를 제외한 부분을 재귀 호출하여 리턴한다.
        이외의 경우
            자릿수에 따라서 알맞은 단위를 추가한다.
            이후 남은 부분을 재귀 호출하여 추가한다.
            최종 결과를 리턴한다.

여기서 한 가지 고려되지 않은 부분이 있습니다. 그것은 일의 자리를 제외하고 1이 올 경우 읽지 않는다는 점입니다. 현재까지의 의사 코드로 소스 코드를 작성하면 100이 들어오면 일백이라 읽게 됩니다. 그 때문에 이 부분을 추가해 줘야 합니다.
이외의 경우 부분에 앞자리 숫자가 1인 경우에 읽지 않는다는 것을 추가하도록 하겠습니다.

    이외의 경우
        가장 앞자리 숫자를 확인한다.
        만일 가장 앞자리 숫자가 0인 경우
            앞자리를 제외한 부분을 재귀 호출하여 리턴한다.
        이외의 경우
            만일 가장 앞자리 숫자가 1인 경우
                읽지 않고 넘어간다.
            이외의 경우
                읽고 넘어간다.
            자릿수에 따라서 알맞은 단위를 추가한다.
            이후 남은 부분을 재귀 호출하여 추가한다.
            최종 결과를 리턴한다.

한편, 네 자리보다 많은 숫자가 들어오는 경우도 추가로 처리를 해 줘야 합니다.
만일 네 자리씩 끊은 부분 중에서 0000과 같은 부분이 있으면 단위가 필요하지 않습니다. 따라서, 이 점을 고려해야 합니다.
이것을 고려하기 위해서 0000이 어떻게 읽어지는지 따져봐야 합니다. 0000의 경우는 앞자리가 0이기 때문에 000이 재귀 호출되며, 다시 00000을, 000을 재귀 호출합니다. 그리고 0의 경우는 빈 문자열을 리턴하기 때문에 결국 0000은 빈 문자열을 리턴하게 됩니다. 즉, 빈 문자열인 경우 넘어가는 코드를 추가하면 됩니다.
이를 처리하는 부분을 추가하면 다음과 같습니다.

이외의 경우
    뒤에서부터 네 자리씩 숫자를 끊는다.
    각각의 끊은 부분에 대해 다음을 수행한다.
        그 부분에 대해 재귀 호출하여 결과를 리턴받는다.
        만일 리턴받은 결과가 빈 문자열인 경우
            다음 부분으로 넘어간다.
        이외의 경우
            최종 결과에 리턴받은 결과를 추가한다.
            최종 결과에 이 부분에 해당하는 단위를 추가한다.
    최종 결과를 리턴한다.

전체 의사 코드는 다음과 같습니다.

만일 네 자리 이하의 숫자가 입력될 경우
    만일 숫자가 한 자리일 경우
        만일 숫자가 0인 경우
            빈 문자열을 리턴한다.
        이외의 경우
            한 자리 숫자를 읽어서 리턴한다.
    이외의 경우
        가장 앞자리 숫자를 확인한다.
        만일 가장 앞자리 숫자가 0인 경우
            앞자리를 제외한 부분을 재귀 호출하여 리턴한다.
        이외의 경우
            만일 가장 앞자리 숫자가 1인 경우
                읽지 않고 넘어간다.
            이외의 경우
                읽고 넘어간다.
            자릿수에 따라서 알맞은 단위를 추가한다.
            이후 남은 부분을 재귀 호출하여 추가한다.
            최종 결과를 리턴한다.
이외의 경우
    뒤에서부터 네 자리씩 숫자를 끊는다.
    각각 끊은 부분에 대해 다음을 수행한다.
        그 부분에 대해 재귀 호출하여 결과를 리턴받는다.
        만일 리턴받은 결과가 빈 문자열인 경우
            다음 부분으로 넘어간다.
        이외의 경우
            최종 결과에 리턴받은 결과를 추가한다.
            최종 결과에 이 부분에 해당하는 단위를 추가한다.
    최종 결과를 리턴한다.

소스 코드 작성

의사 코드를 바탕으로 실제로 코드를 작성해 보도록 하겠습니다. 먼저 한글을 사용하기 위해서 코드의 가장 첫 줄에 #-*- coding: utf-8 -*-을 추가해 줘야 합니다. 이 방식은 인코딩 방식을 utf-8로 바꾸어 유니코드를 사용할 수 있게 합니다. 한글 사용만이 목적일 경우 #-*- coding: euc-kr -*-를 사용해도 됩니다.

입력은 raw_input()으로 받도록 하겠습니다. input()의 경우, 숫자 앞에 0이 있으면 들어온 숫자를 8진수로 해석하기 때문에 부적합합니다. 예를 들어 070을 입력하면 56으로 인식하는 것을 알 수 있습니다.
이제 입력 데이터를 어떻게 처리할지 생각해 봐야 합니다. 이 경우, 다루기 쉽도록 입력받은 숫자를 자리별로 나누어 리스트로 만들도록 하겠습니다. 그리고 이 처리를 위해 지능형 리스트를 사용하도록 하겠습니다.
아래 코드는 입력을 받는 함수입니다.

def get_input():
    """
    입력을 받아 처리하는 함수입니다.
    입력받은 숫자를 자리별로 나누어 리스트를 만듭니다.
    """
    return [int(i) for i in raw_input()]

이제 한 자리 숫자를 어떻게 읽을 것인지 생각해 봐야 합니다. 딕셔너리를 이용해 각 숫자를 key로, 읽은 결과를 value로 해줘도 되지만, 리스트의 index로 각 숫자를 이용해도 됩니다. 저는 리스트를 사용했습니다.
아래는 이 처리를 위해서 만든 리스트입니다.

ONE_DIGIT = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'] #한 자릿수 숫자를 읽은 것

0인 경우의 빈 문자열을 리턴한다.를 처리하기 위해서 0에 해당하는 문자열은 공백으로 설정하였습니다.

이제 단위를 어떻게 처리할지 생각해야 합니다. 단위의 종류는 두 개로 나눌 수 있습니다. 하나는 4자리 이하 숫자를 읽을 때 사용하는 '작은 단위'들이고, 다른 하나는 4자리보다 큰 숫자를 읽을 때 사이를 연결하는 '큰 단위'들입니다.
단위는 크기가 커지는 순서대로 리스트에 저장하기로 하겠습니다. 작은 단위들은 UNIT_S라는 리스트에, 큰 단위들은 UNIT_B라는 리스트에 저장하겠습니다. 그리고 네 자리마다 띄어서 읽는 것을 한 번에 처리하기 위해, 큰 단위들의 뒤에 공백을 하나 추가하였습니다. 이제 의사 코드의 사이에 공백을 두고 연결한다. 부분을 추가로 처리할 필요가 없어집니다. 그리고 UNIT_B[0]을 빈 문자열로 해 두었는데, 이를 통해 4자리보다 큰 숫자를 읽을 때 마지막 네 자리의 처리를 따로 만들 필요가 없어집니다.

UNIT_S = ['십', '백', '천'] #네 자리 이하의, 작은 단위들
UNIT_B = ['', '만 ', '억 ', '조 ',
          '경 ', '해 ', '자 ', '양 ',
          '구 ', '간 ', '정 ', '재 ',
          '극 ', '항하사 ', '아승기 ', '나유타 ',
          '불가사의 ', '무량대수 '] #네 자리 이상의, 큰 단위들

이제 본격적으로 숫자를 읽는 함수를 작성하겠습니다. 숫자를 한글로 읽어준다는 점에서 함수 이름은 num_kor_read로 하도록 하겠습니다. 이 함수는 각 자리의 숫자를 가지는 리스트를 입력으로 받아서 문자열을 돌려줍니다. 먼저 자릿수가 4 이하일 때를 처리하면 다음과 같습니다.

def num_kor_read(num): #num은 각 자리 숫자들의 리스트
    """
    숫자를 입력받아 한글로 읽은 표현을 리턴합니다.
    입력되는 숫자는 각 자리 숫자들로 이루어진 리스트입니다.
    한글로 읽은 표현을 문자열로 리턴합니다.
    다만, 입력되는 숫자가 0일 경우에는 빈 문자열을 리턴합니다.
    먼저 자릿수가 1일 때를 처리하고 이를 바탕으로 4일 때까지를 재귀로 처리합니다.
    그리고 자릿수가 그보다 클 때는 네 자리씩 끊어서 재귀로 처리합니다.
    """
    digits = len(num) # 숫자 자릿수
    if digits == 1:
        return ONE_DIGIT[num[0]] #한 자릿수 숫자의 경우 one_digit에서 해당하는 숫자를 돌려준다.
    if digits <= 4: #숫자 자릿수가 4자리 이하일 경우 가장 앞자리를 뽑아 먼저 읽고 나머지를 처리한다.
        head = num[0] #가장 앞자리 수
        if head == 0: #가장 앞자리가 0인 경우
            return num_kor_read(num[1:])
        part_read = []
        if head != 1: #가장 앞자리가 1이 아닌 경우
            part_read.append(ONE_DIGIT[head])
        part_read.append(UNIT_S[digits-2]) #단위 추
        part_read.append(num_kor_read(num[1:])) #뒷부분을 재귀를 이용해 읽는다.
        return ''.join(part_read)

먼저 자릿수가 1자리인 경우를 처리하고, 이후 이외의 자릿수가 4자리 이하인 경우를 재귀 호출로 처리하였습니다. 가장 앞자리 숫자가 1이 아닌 경우에 추가로 리스트를 사용해 읽은 부분을 저장한 후 마지막에 ''.join()을 이용해 문자열로 만들었습니다. ''.join()의 경우는 시간복잡도가 O(1)로, 문자열 덧셈보다 빠릅니다.

이제 자릿수가 4보다 큰 경우를 처리하겠습니다. 의사 코드에서는 뒤에서부터 네 자리씩 끊어서 처리한다고 하였습니다. 다만 실제로 구현하려면 자릿수가 4의 배수가 아닌 경우의 처리가 중요해집니다. 사용할 수 있는 방법 중의 하나는 나머지가 존재할 경우에 따로 처리하는 방법입니다. 그러나 저는 앞자리에 적당한 개수의 0을 추가해서 4의 배수로 만드는 방법을 쓰도록 하겠습니다. 0은 읽지 않으므로 전체 결과에는 영향이 없습니다.

    else: #가독성을 위해 else문을 사용한다.
        #자릿수가 4의 배수가 아니면 앞자리에 0을 추가해 4의 배수로 만든다.
        if digits%4 != 0:
            longer_num = [0 for i in xrange(4-digits%4)]
            digits += 4-digits%4
        else:
            longer_num = []
        longer_num.extend(num)
        part_read = []
        for i in xrange(digits/4): #앞에서부터 네 자리씩 처리한다.
            temp = num_kor_read(longer_num[4*i:4*i+4])
            if temp == '': continue #0000의 예외처리
            part_read.append(temp)
            part_read.append(UNIT_B[digits/4-i-1]) #단위 추가
        return ''.join(part_read)

앞자리에 0을 추가하기 위해서 num.insert(0,0)을 사용할 수도 있지만 이 방법은 매번 리스트를 새로 만들기 때문에 상당히 느립니다. 때문에, 새로운 리스트를 만들어 .extend()를 사용하였습니다.

마지막으로 main() 함수를 작성하겠습니다. 입력을 받고, 0의 예외처리까지 포함해 한글을 출력하는 코드입니다.

def main():
    """
    메인 함수입니다. 입력을 받고, 그 입력에 따라 한글로 읽은 표현을 리턴합니다.
    들어온 숫자가 0인 경우에는 따로 처리합니다.
    """
    num = get_input()
    if num == [0]:
        return '영'
    return num_kor_read(num)

전체 소스 코드는 다음과 같습니다.

#-*- coding: utf-8 -*-
"""
==============================
숫자를 입력할 경우 한글로 읽은 표현을 출력하는 함수입니다.
기본적으로 네 자리 이하는 따로 처리하고 그 이상은 재귀 호출로 처리합니다.
==============================
제작자: NbelowP
==============================
"""
ONE_DIGIT = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'] #한 자릿수 숫자를 읽은 것
UNIT_S = ['십', '백', '천'] #네 자리 이하의, 작은 단위들
UNIT_B = ['', '만 ', '억 ', '조 ',
          '경 ', '해 ', '자 ', '양 ',
          '구 ', '간 ', '정 ', '재 ',
          '극 ', '항하사 ', '아승기 ', '나유타 ',
          '불가사의 ', '무량대수 '] #네 자리 이상의, 큰 단위들
def get_input():
    """
    입력을 받아 처리하는 함수입니다.
    입력받은 숫자를 자리별로 나누어 리스트를 만듭니다.
    """
    return [int(i) for i in raw_input()]
def num_kor_read(num): #num은 각 자리 숫자들의 리스트
    """
    숫자를 입력받아 한글로 읽은 표현을 리턴합니다.
    입력되는 숫자는 각 자리 숫자들로 이루어진 리스트입니다.
    한글로 읽은 표현을 문자열로 리턴합니다.
    다만, 입력되는 숫자가 0일 경우에는 빈 문자열을 리턴합니다.
    먼저 자릿수가 1일 때를 처리하고 이를 바탕으로 4일 때까지를 재귀로 처리합니다.
    그리고 자릿수가 그보다 클 때는 네 자리씩 끊어서 재귀로 처리합니다.
    """
    digits = len(num) # 숫자 자릿수
    if digits == 1:
        return ONE_DIGIT[num[0]] #한 자릿수 숫자의 경우 one_digit에서 해당하는 숫자를 돌려준다.
    if digits <= 4: #숫자 자릿수가 4자리 이하일 경우 가장 앞자리를 뽑아 먼저 읽고 나머지를 처리한다.
        head = num[0] #가장 앞자리 수
        if head == 0: #가장 앞자리가 0인 경우
            return num_kor_read(num[1:])
        part_read = []
        if head != 1: #가장 앞자리가 1이 아닌 경우
            part_read.append(ONE_DIGIT[head])
        part_read.append(UNIT_S[digits-2]) #단위 추
        part_read.append(num_kor_read(num[1:])) #뒷부분을 재귀를 이용해 읽는다.
        return ''.join(part_read)
    else: #가독성을 위해 else 문을 사용한다.
        #자릿수가 4의 배수가 아니면 앞자리에 0을 추가해 4의 배수로 만든다.
        if digits%4 != 0:
            longer_num = [0 for i in xrange(4-digits%4)]
            digits += 4-digits%4
        else:
            longer_num = []
        longer_num.extend(num)
        part_read = []
        for i in xrange(digits/4): #앞에서부터 네 자리씩 처리한다.
            temp = num_kor_read(longer_num[4*i:4*i+4])
            if temp == '':
                continue #0000의 예외처리
            part_read.append(temp)
            part_read.append(UNIT_B[digits/4-i-1]) #단위 추가
        return ''.join(part_read)
def main():
    """
    메인 함수입니다. 입력을 받고, 그 입력에 따라 한글로 읽은 표현을 리턴합니다.
    들어온 숫자가 0인 경우에는 따로 처리합니다.
    """
    num = get_input()
    if num == [0]:
        return '영'
    return num_kor_read(num)
if __name__ == '__main__':
    print main()

개발 환경에서 잘 실행되는 것을 확인하고 명령 프롬프트에서 실행시켜 보기로 했습니다.
그런데...

C:\Users\User\Desktop>python number_read.py
10070320449120001403
泥쒖튌寃??쇰갚?댁떗議??ъ쿇?щ갚援ъ떗?쇱뼲 ?댁쿇留?泥쒖궗諛깆궪Traceback (most recent call last):
  File "number_read.py", line 84, in <module>
    print main()
IOError: [Errno 0] Error

에러가 납니다!


어째서 에러가 나는가?

사실 이건 Python 2라서 존재하는 문제점입니다. Python 2에서 한글 입출력을 하기는 상당히 복잡합니다. Python 2에서는 기본 인코딩 방식이 ascii로, 이 때문에 유니코드를 다루게 되면 상당히 복잡해집니다. 반면 Python 3에서는 기본 인코딩 방식이 utf-8이라서 한글을 다룰 때 큰 문제가 없습니다. 그 때문에 한글을 다루게 되면 되도록 Python 3을 사용해야 합니다.

원래대로라면, #-*- coding: utf-8 -*-에 의해서 한글 출력이 똑바로 될 것이라고 추측했지만 그렇지가 않습니다. 이것은 아마 Python 2에서는 함수의 입/출력도 ascii가 기본이기 때문이라고 추측합니다. 정확한 원인을 아시는 분 있으시면 알려주시면 감사하겠습니다.

이렇게 되었으니 에러를 없애기 위해 필요한 작업을 추가로 하도록 하겠습니다. 일단, 문자열이 유니코드임을 알려주기 위해 따옴표 앞에 u를 추가해 주는 방법이 있습니다. 예를 들어, '한글'이라고 쓰던 것을 u'한글'이라고 쓰는 것이죠. 실제로, 이렇게 수정하면 코드가 정상적으로 동작합니다.

하지만 이 과정은 지나치게 길고 복잡합니다. 문자열이 많으면 엄청난 노동이 됩니다. 특히, 코드의 UNIT_B와 같은 경우에 이것을 하는 것은 정말 힘들죠. 따라서, map()unicode()를 추가로 사용하는 것을 생각해 볼 수 있습니다. map()의 경우 함수형 프로그래밍 언어로부터 받아들여진 개념으로, map(function, iterable)와 같은 형태로 쓰게 됩니다. 이 함수의 결괏값은 iterable 객체의 각 원소에 function을 적용한 값들을 모아놓은 리스트입니다(파이썬 3에서는 리스트 대신 튜플을 결괏값으로 받습니다). 그리고 리스트 역시 iterable이기 때문에 사용 가능합니다. unicode()의 경우는 문자열을 유니코드로 바꿉니다. 이제 리스트의 문자열에 대해서 훨씬 짧게 처리가 가능해졌습니다.

그러나 실제로 이렇게 사용한 후 실행하면 에러가 나는 것을 알 수 있습니다. 이것은 unicode() 함수가 가지는 문제점으로, 기본적으로 저 함수의 입력도 ascii이기 때문입니다. 이를 해결하기 위해서 사용할 수 있는 방법은 일일이 인코딩 방식을 명시하는 것입니다. 하지만 이렇게 하려면 새로운 함수를 정의하거나 람다 표현식을 사용해야 합니다. 대신, 더 효율적인 방법이 있습니다. sys 모듈에서 sys.setdefaultencoding()을 통해 기본 인코딩을 바꾸면 됩니다. 다만, 그냥은 사용할 수 없고, 반드시 reload(sys)를 통해 sys모듈을 다시 불러와야 합니다.

이제 이 모든 처리를 끝내면 동작하는 코드를 얻을 수 있습니다. 이 코드는 다음과 같습니다.

#-*- coding: utf-8 -*-
"""
==============================
숫자를 입력할 경우 한글로 읽은 표현을 출력하는 함수입니다.
기본적으로 네 자리 이하는 따로 처리하고 그 이상은 재귀 호출로 처리합니다.
==============================
제작자: NbelowP
==============================
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
ONE_DIGIT = map(unicode,['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구']) #한 자릿수 숫자를 읽은 것
UNIT_S = [u'십', u'백', u'천'] #네 자리 이하의, 작은 단위들
UNIT_B = map(unicode,['', '만 ', '억 ', '조 ',
          '경 ', '해 ', '자 ', '양 ',
          '구 ', '간 ', '정 ', '재 ',
          '극 ', '항하사 ', '아승기 ', '나유타 ',
          '불가사의 ', '무량대수 ']) #네 자리 이상의, 큰 단위들
def get_input():
    """
    입력을 받아 처리하는 함수입니다.
    입력받은 숫자를 자리별로 나누어 리스트를 만듭니다.
    """
    return [int(i) for i in raw_input()]
def num_kor_read(num): #num은 각 자리 숫자들의 리스트
    """
    숫자를 입력받아 한글로 읽은 표현을 리턴합니다.
    입력되는 숫자는 각 자리 숫자들로 이루어진 리스트입니다.
    한글로 읽은 표현을 문자열로 리턴합니다.
    다만, 입력되는 숫자가 0일 경우에는 빈 문자열을 리턴합니다.
    먼저 자릿수가 1일 때를 처리하고 이를 바탕으로 4일 때까지를 재귀로 처리합니다.
    그리고 자릿수가 그보다 클 때는 네 자리씩 끊어서 재귀로 처리합니다.
    """
    digits = len(num) # 숫자 자릿수
    if digits == 1:
        return ONE_DIGIT[num[0]] #한 자릿수 숫자의 경우 one_digit에서 해당하는 숫자를 돌려준다.
    if digits <= 4: #숫자 자릿수가 4자리 이하일 경우 가장 앞자리를 뽑아 먼저 읽고 나머지를 처리한다.
        head = num[0] #가장 앞자리 수
        if head == 0: #가장 앞자리가 0인 경우
            return num_kor_read(num[1:])
        part_read = []
        if head != 1: #가장 앞자리가 1이 아닌 경우
            part_read.append(ONE_DIGIT[head])
        part_read.append(UNIT_S[digits-2]) #단위 추
        part_read.append(num_kor_read(num[1:])) #뒷부분을 재귀를 이용해 읽는다.
        return ''.join(part_read)
    else: #가독성을 위해 else 문을 사용한다.
        #자릿수가 4의 배수가 아니면 앞자리에 0을 추가해 4의 배수로 만든다.
        if digits%4 != 0:
            longer_num = [0 for i in xrange(4-digits%4)]
            digits += 4-digits%4
        else:
            longer_num = []
        longer_num.extend(num)
        part_read = []
        for i in xrange(digits/4): #앞에서부터 네 자리씩 처리한다.
            temp = num_kor_read(longer_num[4*i:4*i+4])
            if temp == '':
                continue #0000의 예외처리
            part_read.append(temp)
            part_read.append(UNIT_B[digits/4-i-1]) #단위 추가
        return ''.join(part_read)
def main():
    """
    메인 함수입니다. 입력을 받고, 그 입력에 따라 한글로 읽은 표현을 리턴합니다.
    들어온 숫자가 0인 경우에는 따로 처리합니다.
    """
    num = get_input()
    if num == [0]:
        return u'영'
    return num_kor_read(num)
if __name__ == '__main__':
    print main()

더 간단한 방법은 없는가?

에러를 없애는 데는 성공했습니다. 다만 코드가 상당히 복잡해졌습니다. 게다가, 이 경우는 리스트에서 대부분의 문자열을 저장하고 있어서 간단히 처리했지만, 함수 내부에서 문자열을 많이 사용하면 일일이 u를 적어줘야 합니다.
때문에, 더 효율적인 방법이 필요합니다.
Python 3에서처럼 Python 2에서도 문자열을 모두 유니코드로 취급하면 모든 문제가 해결됩니다. 어떻게 하면 이것을 가능하게 할 수 있을까요?
Python의 기본 모듈 중에서 __future__이라는 모듈이 있습니다. '미래'라니, 어떤 기능인지 짐작이 가시나요? 그렇습니다. 이 모듈은 Python 2와 3에서 동시에 동작하는 코드를 만들기 위한 기능들을 포함하는 함수입니다! 그 말은, 저 모듈의 기능 중에서 문자열을 유니코드 문자열로 인식하게 하는 기능이 존재한다는 것을 의미합니다!
이제, sys모듈이나 u, unicode없이도 한글을 출력하게 할 수 있습니다(다만, #-*- coding: utf-8 -*-은 있어야 합니다). 단순히 첫 줄에 다음을 추가합시다.

from __future__ import unicode_literals

unicode_literals라는 이름에서 알 수 있듯이, 이제 모든 문자열이 유니코드 문자열로 취급됩니다! 만세! 모든 문제가 해결됐습니다!


맺음말

첫 번째 포스팅인데 잘 되었는지 모르겠네요. 사실, 본문에서 사용한 코드는 한번 작성한 이후 다시 작성한 코드입니다. 처음 작성한 코드는 네 자리 이하는 각각 나눠서 처리하도록 작성했었습니다. 그리고 한글 출력 문제를 해결하지 못해, 개발 환경에서만 똑바로 돌아가는 코드였습니다.

이렇게 글을 적으면서 최대한 깔끔하게 코드를 작성하려고 노력하였습니다. 그래도 아쉬움이 남네요. 혹여 더 깔끔한 방법을 아시는 분은 알려주시면 감사하겠습니다. 그리고 혹시 있을지 모를 코드 오류도 알려주시면 감사하겠습니다.

나중에 이 코드를 조금 수정해서 Python 3 코드를 만들어 보기도 해야겠습니다. Python 3 문법 공부 겸... 해서 말이죠.
그나저나 재귀를 이용한 방식으로 의사 코드를 짰기 때문에 Haskell로도 구현할 수 있지 않을까 생각하는 중입니다. 아직 Haskell 문법을 기초적인 것밖에 몰라서 못 하지만, 나중에 더 공부한 후에 Haskell로 한번 코드를 짜 봐야겠네요.

그리고 질문 하나, 마크다운 문법 중에서 특정 부분을 접었다 폈다 할 수 있게 하는 문법이 있나요? 중간 소스코드가 너무 긴 것 같아서 질문드립니다.

포스팅에 대해 비판, 오타 및 오류 지적 등은 언제나 환영입니다.

Sort:  

첫포스팅치고 퀄리티가 굉장히 훌룡하십니다. 마크다운 문법에서 접었다 폈다 할 수 있는 문법은 없는걸로 알고있습니다. 앞으로 스팀잇에서 좋은 활동 펼쳐나가셨으면 좋겠습니다. @홍보해

칭찬과 답변 감사드립니다. 앞으로도 좋은 글을 적기 위해서 열심히 노력하겠습니다.

@nbelowp님 안녕하세요. 개부장 입니다. @julianpark님이 이 글을 너무 좋아하셔서, 저에게 홍보를 부탁 하셨습니다. 이 글은 @krguidedog에 의하여 리스팀 되었으며, 가이드독 서포터들로부터 보팅을 받으셨습니다. 축하드립니다!

반갑습니다. 열심은 정말 아름답습니다.
저도 배우고 갑니다.
새해 복 많이 받으세요~

배움을 드렸다니 영광이네요. 저도 반갑습니다.
ohnamu님도 새해 복 많이 받으세요!

Congratulations! This post has been upvoted from the communal account, @minnowsupport, by NbelowP from the Minnow Support Project. It's a witness project run by aggroed, ausbitbank, teamsteem, theprophet0, someguy123, neoxian, followbtcnews, and netuoso. The goal is to help Steemit grow by supporting Minnows. Please find us at the Peace, Abundance, and Liberty Network (PALnet) Discord Channel. It's a completely public and open space to all members of the Steemit community who voluntarily choose to be there.

If you would like to delegate to the Minnow Support Project you can do so by clicking on the following links: 50SP, 100SP, 250SP, 500SP, 1000SP, 5000SP.
Be sure to leave at least 50SP undelegated on your account.