2011년 5월 11일 수요일

[Python] 유니코드와 UTF-8간의 전쟁(?)

Python에서 Unicode와 UTF-8에 관한 이야기를 언급한 적이 있었는데, 처리과정에서 발생하는 오류를 이용해 주의점을 한번 환기해 볼 까 한다.

예제는 UTF-8 환경의 터미널에서 python 인터프리터를 사용함을 가정한다.

우선, Unicode와 UTF-8은 타입이 다르다.
>>> a = u'한글'
>>> b = '한글'
>>> type(a).__name__
'unicode'
>>> type(b).__name__
'str'
기본적인 지식이다.

그래서 둘 간의 비교는 뭔가 문제가 있다.
>>> a == b
__main__:1: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
False

정확히 비교하려면 타입을 맞춰야 할 것이다.
>>> a.encode('utf-8') == b
True

다행히도 print문에서는 unicode이라면 알아서 인코딩 시켜서 출력시켜 준다. 기준은 뭔지는 모르겠지만...
>>> print a
한글
>>> print b
한글

하지만 unicode는 str로 문자열로 변환할 수 없다.
>>> str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

utf-8으로 인코딩 할 경우 타입은 str이 되기 때문에 뭐 차이는 없겠지.
>>> str(b)
'\xed\x95\x9c\xea\xb8\x80'
>>> str(b) == b
True

앞에서 print의 경우 문제는 없다고 했는데 한번 더...
>>> print '%s' % a
한글
>>> print '%s' % b
한글

하지만 둘 다 한번에 print하려면...
>>> print '%s -- %s' % (a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 0: ordinal not in range(128)

그렇다면 문자열을 합치는 건 될까. 안되겠지.
>>> print a+b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xed in position 0: ordinal not in range(128)

이 쯤 되면 슬슬 눈치채야 할 것이다. Unicode이던 utf-8이던 타입은 한 가지만으로 유지하도록 encode/decode해서 가지고 있어야 한다는 점이다. 그래야 추후 프로세스에서 문제를 일으킬 가능성이 사라진다.

한 가지 충고를 하자면, Unicode는 str이 아니기 때문에 문자열 관련 함수나 메서드를 사용할 때 문제가 생길 수 있다. 따라서 가급적이면 utf-8으로만 유지하는게 좋은 선택인 것 같다.

위 문장은 착오가 있었다. 소스코드의 경우라면 코드 최상단에 아래와 같은 UTF-8을 사용한다는 표식을 적어두면 내부에서는 모두 Unicode도 처리하면 문제가 없다.
# coding: utf-8
물론 인터프리터는 쉘의 환경을 상속받았기 때문에 위와 같이 해야 된다.

댓글 없음 :