작성자: admin 작성일시: 2016-06-14 23:09:51 조회수: 7526 다운로드: 197
카테고리: Python 태그목록:

파이썬의 문자열 인코딩

문자와 인코딩

파이썬 뿐 아니라 모든 컴퓨터에서 문자는 2진 숫자의 열 즉, 바이트 열(byte sequence)로 바뀌어 저장된다. 이를 인코딩(encoding)이라고 하며 어떤 글자를 어떤 숫자로 바꿀지에 대한 규칙을 인코딩 방식이라고 한다.

가장 기본이 되는 인코딩 방식은 아스키(ASCII) 방식이다.

한글의 경우 과거에는 EUC-KR 방식이 많이 사용되기도 했으나 최근에는 CP949 방식이 더 많이 사용된다.

유니코드

인코딩 방식이 글자마다 혹은 회사마다 다르기 때문에 발생하는 문제를 해결하고자 유니코드라는 것이 만들어졌다. 유니코드에서는 다음과 같은 표준을 정했다.

  • 유니코드 코드 포인트 (unicode code point)
  • 유니코드 인코딩 (UTF-8, UTF-16, UTF-32, ...)

유니코드 코드 포인트(code point) 또는 코드 포지션(code position)은 최대 4바이트의 숫자로 전 세계 모든 글자를 대응시킨 것이다. 파이썬 내부에서 문자를 메모리에 저장할 때 주로 사용된다(파이썬 3.3부터는 PEP 393에 의해 바뀌었다).

유니코드 인코딩은 실제로 유니코드 문자를 바이트 열로 바꾸어 파일 등에 저장할 때 사용되는 방식으로 영문 알파벳에 대해 ASCII 인코딩 방식과 호환하며 크기, 정렬 등의 문제를 고려하여 설계되었다. 과거의 EUC-KR, CP949 등은 한글이라는 문자에 대해 저장할 값을 지정하는 방식이지만, 유니코드 인코딩 방식은 유니코드 코드 포인트에 대해 저장할 값을 지정하는 방식이므로 유니코드 코드 포인트를 가지는 모든 글자를 하나의 방식으로 저장할 수 있다. UTF-8은 현재 가장 많이 사용되는 유니코드 인코딩 방식의 하나이다.

파이썬의 문자열 자료형

파이썬 3에서는 기본 문자열의 자료형이 유니코드이며 자료형의 이름은 str 이다. 그리고 바이트열로 저장하기 위한 bytearray 자료형도 지원한다. 다음과 같은 명령으로 바이트 열로 상호 변환할 수 있다.

  1. bytearray 명령어로 유니코드를 바이트열로 변환
  2. b 접두사를 이용한 바이트열 리터럴(literal) 생성

파이썬 2에서는 반대로 기본 문자열 자료형이 바이트열이고 str 도 바이트열을 의미한다. 그리고 유니코드 포인트 방식으로 저장하기 위한 unicode 타입도 지원한다. str 타입과 unicode 타입간의 변환을 위해서는 다음과 같은 방법을 사용한다.

  1. unicode 명령어로 유니코드 코드 포인트 반환
  2. u 접두사를 이용한 유니코드 리터럴(literal) 생성

유니코드로 한글을 저장해야 하는 이유

만약 한글 문자열을 단순히 화면에 출력하거나 파일로 내보내기만 하는 것인 목적이라면 굳이 유니코드를 이용할 필요는 없다. 그러나 한글 문자열을 분석해야 한다면 반드시 유니코드로 변환해야 한다. 왜냐하면 유니코드가 아닌 인코딩된 바이트 열은 한글의 글자 수를 세거나 글자 단위로 분리하는 것이 어렵기 때문이다.

예를 들어 "가"라는 글자가 있을 때, 이 글자가 유니코드이면 문자열의 원소 수는 1로 계산된다. 하지만 바이트 열이면 인코딩 방식에 따라 달라질 수 있다.

In:
u = "가"
len(u)
Out:
1
In:
b = bytearray("가", 'cp949')
len(b)
Out:
2

바이트열이면 글자를 한글자씩 분리할 수도 없다.

In:
u1 = "ABC"
u2 = "가나다"
print(len(u1), len(u2))
print(u1[0], u1[1], u1[2])
print(u2[0], u2[1], u2[2])
3 3
A B C
가 나 다
In:
b1 = bytearray("ABC", 'cp949')
b2 = bytearray("가나다", 'cp949')
print(len(b1), len(b2))
print(chr(b1[0]), chr(b1[1]), chr(b1[2]))
print(chr(b2[0]), chr(b2[1]), chr(b2[2]), chr(b2[3]))
3 6
A B C
° ¡ ³ ª

인코딩과 디코딩

유니코드를 바이트 열로 변환할 때는 인코드 encode 메서드를, 바이트 열을 유니코드로 변환할 때는 디코드 decode 메서드를 사용한다. 그런데 인코딩할 때는 출력 장치(콘솔, 주피터 노트북)가 어떤 인코딩을 지원하는지를 미리 알고 있어야 한다. 만약 출력 장치가 지원하지 않는 방식으로 인코딩하면 화면에는 이상한 글자만 보이게 된다.

예를 들어 현재 필자가 이 코드를 실행하는 주피터 노트북에서는 utf-8 인코딩을 지원한다. 따라서 CP949로 인코딩한 바이트 열은 글자가 깨져서 보인다.

In:
u = "가나다"
type(u)
Out:
str
In:
b1 = u.encode("cp949")
type(b1)
Out:
bytes
In:
b1
Out:
b'\xb0\xa1\xb3\xaa\xb4\xd9'
In:
b2 = u.encode("euc-kr")
type(b2)
Out:
bytes
In:
b2
Out:
b'\xb0\xa1\xb3\xaa\xb4\xd9'
In:
b3 = u.encode("utf-8")
type(b3)
Out:
bytes
In:
b3
Out:
b'\xea\xb0\x80\xeb\x82\x98\xeb\x8b\xa4'

디코딩할 때도 바이트열이 원래 인코딩된 방식으로 디코딩해야 제대로 된 문자열을 얻을 수 있다.

In:
b1.decode("cp949")
Out:
'가나다'
In:
b2.decode("euc-kr")
Out:
'가나다'
In:
b3.decode("utf-8")
Out:
'가나다'
In:
b1.decode("utf-8")
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
 in ()
----> 1 b1.decode("utf-8")

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 0: invalid start byte
In:
b3.decode("cp949")
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
 in ()
----> 1 b3.decode("cp949")

UnicodeDecodeError: 'cp949' codec can't decode byte 0x80 in position 2: illegal multibyte sequence

인코딩 설정

콘솔 입력이나 파일은 유니코드 포인트로 동작하는 것이 아니라 인코딩된 바이트 열로 동작한다. 따라서 파이썬이 콘솔로 입력된 코드나 스크립트 코드를 읽을 때는 이 코드가 어떤 방식으로 인코딩되어 있는지 알고 있어야 한다. 콘솔 입력이면 환경 변수 PYTHONIOENCODING으로 지정하고, 스크립트(파일)이면 첫 줄에 다음처럼 인코딩 설정 정보를 넣어 주면 된다.

#-*- coding: utf-8 -*-

질문/덧글

아직 질문이나 덧글이 없습니다. 첫번째 글을 남겨주세요!