파이썬에서 한글이 깨진다고요? – 파이썬의 한글 입출력과 인코딩에 대해

파이썬의 한글 인코딩에 대해

파이썬의 대화형 인터프리터를 사용하다보면 한글 인코딩의 함정에 빠지기 쉬운데 이를테면 소스를 그대로 해석기로 실행하는 경우에는 인코딩 에러가 안나던 것이, IDLE을 통해서 실행해보면 오류가 난다거나 그 반대의 경우가 있다. 이렇게 이해할 수 없는 상황을 어떻게 해야할까?

몇년 전이라면 그것은 MS의 잘못이거나 파이썬의 잘못이었다. 하지만 윈도에서 한글로 된 데이터를 다뤄야 하는데 파이썬 2를 쓰고 있다면 그것은 매우 높은 확률로 당신의 잘못이다.

파이썬에서 한글이 깨진다고요? – 파이썬의 한글 입출력과 인코딩에 대해 더보기

유니코드 한글의 각 음소를 분리하기

유니코드에서 한글은 0xAC00에서 0xD7A3 사이의 코드 값을 갖는다. 각 16진수값은 10진수로 표시하면 44032와 55203으로 총 11,172개이다. 유니코드 내 한글은 초/중/종성의 각 음소의 조합으로 표현된다. 즉 초성 19개, 중성 21개, 종성 28개를 조합하여 하나의 글자가 되는 것이다. 따라서 각 초,중,종성의 위치값을 계산하여 최종적으로 만들어지는 글자의 코드가 생성된다. 이 때 들어가는 값은 위치 값으로 0~해당 음소의 개수-1 만큼의 인덱스를 의미한다.

((초성 * 21) + 중성) * 28 + 종성 + 0xAC00

이를 역산하면 어떤 문자의 코드값으로부터 각 음소의 인덱스를 구할 수 있다.  즉 각 음소 중에서 몇 번 째 글자인지를 알 수 있게 된다.

초성 = ((문자코드 – 0xAC00) / 28) / 21
중성 = ((문자코드 – 0xAC00) / 28) % 21
종성 = (문자코드 – 0xAC00) % 28

이 때 계산된 값은 각 음소가 음소 문자 중에서 몇 번째 문자인지를 나타내는 위치값이다.
초성의 자모 코드 시작값은 0x1100, 중성은 0x1161, 종성은 0x11A8 이므로 이를 각각 더한다. 특히 종성이 없는 경우가 있기 때문에 종성에는 1을 뺀다.

초성의 자모코드 = 초성인덱스 + 0x1100
중성의 자모코드 = 중성인덱스 + 0x1161
종성의 자모코드 = 종성인덱스 + 0x11A8 – 1

구현

NSString에서 문자열 내 특정 글자를 뽑아오는 일은 characterAtIndex: 메소드를 사용하고 이 때 반환되는 값은 unichar 포맷이 된다. (unichar 는 unsigned short 타입의 변수형이다.)

unichar oneCode = [hangul characterAtIndex:i];

이를 위 과정을 통해 계산해서 초성 (및 중/종성)을 추출한 다음, 이를 다시 NSString으로 만들기 위해서는 stringWithFormat: 메소드를 사용한다. 이 때 포맷팅 파라미터는 %C (대문자)를 사용한다. 소문자 %c를 쓰는 것은 char 타입일 때 이다. (바꿔써도 무리는 없는 것 같더라)

코드

-(NSString *)getFirstCodeWithString:(NSString *)hangul
{
    NSString *result = @"";
    for ( int i=0; i<[hangul length];i++) {
        unichar oneCode = [hangul characterAtIndex:i];
        // 한글일 때만 처리한다.
        if ( oneCode >= 0xAC00 && oneCode <= 0xD7A3 ) {
            unichar firstCode = ((oneCode -0xAC00) / 28)/21;
            firstCode += 0x1100;
            result = [result stringAppendingString:[NSString stringWithFormat:@"%C",firstCode]];
        }
    }
    return result;
}