가끔 파이썬으로 한글을 처리할 때면 곤혹스러워진다. 바로 인코딩때문에 안그래도 없는 시간이 총알처럼 지나가기 때문이다. 인코딩 문제는 시간만 까먹고 해결해봐야 밑져야 본전인 일이다. 3.x 버전과 달리 특히 2.x 버전에서는 유니코드와 다른 문자열들이 혼재해 있어 더더더더더욱 환장할 때가 있다.


여기에 unicode와 UTF-8 간의 분쟁을 정리해보자!


1. unicode와 UTF-8은 뭐야?

사실 UTF-8이 Unicode이다. (-.-) 실제로 위키피디아에 검색을 해보자. UTF-8은 "유니코드를 위한 가변 길이 문자 인코딩 방식 중 하나"라고 되어있다. (여기) 그렇다면 이건 무슨 개소리인가. 사실 파이썬에는 인코딩이라는 무시무시한 불규칙 세상에서 모든 글자를 자유로이 쓰려는 노력이 담겨있다. (맞나...?) 그래서 파이썬에는 두가지 종류의 문자열이 있다. 

 (1) unicode 

 (2) byte code

 여기서 byte code란 정말 우리가 쓰는 글자열이 어떤 특정한 규칙을 통해 byte로 만들어진 것을 말한다. 그럼 unicode는 무엇이냐고? python에서 unicode란 그저 "문자열"이다. 즉, 컴퓨터에서 쓰는 특정한 규칙을 거치기 전, unicode라는 거대한 체계에서 정의된 "문자열"일 뿐이다.


그럼 두 개의 차이는?


byte code는 출력이 자유롭다. 만약 여러분이 한글 윈도우에서 작업하고 있다면, system의 location code는 cp949 일 것이다. (거의 99.9%) 하지만 리눅스나 OSX일 경우, system의 location code는 UTF-8이 된다. 경험하신 분도 계시겠지만, 같은 문자도 다른 문자열 조합을 이용하면서 byte로 표현되는 방법이 바뀐다. 

좋은 예제는 여기에 가서 Omega가 어떻게 byte로 표현되는지 확인할 수 있다. 간단히 cp949와 UTF-8에서의 A를 찾아봐도 사실 많이 다르다.


UTF-8 에서의 A: 0x41

cp949에서의 A: 0xA3C1


따라서 byte code는 출력이 자유롭지만 현재 컴터의 기본 문자열 조합과 다른 경우, 글자가 깨진다. ㄷㄷㄷㄷ

반면 유니코드 문자열은 출력하거나 파일에 쓰는 방법이 딱히 없다. 그저 "문자열"일 뿐 이진수로 표현되지 않았기 때문이다. 그래서 우리는 문자열 코덱이 필요한거다.


2. python에서 유니코드와 이진문자 사용하기

파이썬에서는 사실 두 종류의 문자를 모두 사용할 수 있기 때문에 더욱 헷갈린다. 어려울 경우 console을 띄우고 차근차근 해보는게 좋다.

우선 이진문자의 경우 

>>> a = "한글"

이렇게 사용하면 된다. 그럼 a는 컴터의 로케일에 맞추어 알아서 문자열이 인코딩된다. 한글 윈도우는 cp-949 나머지는 보통 UTF-8이다.

유니코드로 사용하고 싶다면,

>>> a= u"한글"

이렇게 사용하면 된다. 


모두 출력은 

>>>print a

를 통해서 가능하다. print의 경우 유니코드는 알아서 컴터의 로케일 문자열로 변환해 보여준다. 만약 깨져보이면, 컴터의 로케일 문자열 셋과 다른 것으로 인코딩 된 것일뿐, 문자열이 이상해진건 아니니 걱정안해도 된다.


3. 차이점?

차이점은 있다. 바로 문자열의 비교나 연산(더하기 등등)에서 에러가 난다는 점이다. 생각해보면 조금 당연한데, 유니코드  문자열은 사실 이진문자가 아니므로 컴터가 어떻게 해줄 수가 없다....-.-

print 함수의 경우 착해서 문자열을 알아서 로케일 문자열 셋으로 변환해 보여준 것일 뿐.... 나머지 기능들은 그렇게 착하지 않다.


참고로 이진문자(byte code)끼리 혹은 유니코드 문자열끼리는 비교나 연산이 가능하다.


4. 변환하기

유니코드 문자와 이진 문자 혹은 서로 다른 문자열 셋으로 만들어진 이진문자를 처리해야 할 때가 있다. 이럴 때에는 프로그램 내에서 사용할 문자열의 셋을 정하자. UTF-8이 되었건 유니코드가 되었건 아니면 cp-949나 다른 셋이건 하나 정하고 통일해야 정신을 잃지 않는다.


간단한 함수 두 개만 잘 기억하자.

s = u.encode("UTF-8")

u = s.decode("UTF-8")


유니코드에서 특정 이진문자로 넘어가기 위해선 encode()를 이용한다. 중요한 점은, 여기에 argument로 들어가는 것이 바로 변환하고자 하는 이진문자의 문자열 셋이다. 이건 본인이 그때그때 적당하게 잡아주면 된다.

거꾸로 이진문자에서 유니코드로 넘어가고자 할 때에는 decode()를 이용한다. 여기서도 문자열 셋의 종류가 들어가는데, 이건 바꾸려는 대상인 이진문자의 문자열 셋이다. 이걸 모르면.... 정말 골치아픈데, 사실 한글은 cp949 혹은 UTF-8 둘 중 하나다. 잘못된 문자열 셋을 넣으면 에러나니까 주의!


decode() 함수 이외에도 유니코드로 가는 방법이 한가지 더 있다. u = unicode(s, "UTF-8") 등의 방법으로 유니코드로의 변환이 가능하다.


5, 데이터 쓰기

문자열을 신나게 처리하면 마지막에 기록을 해야 한다. 기록하는 방법 중 가장 간단한 방법은, 

>>> f = open("aaa.txt", 'w')

처럼 파일을 열고 여기에 쓰는 것이다. 해보면 알겠지만 여기에 아무 문자열 셋으로 만들어진 이진문자를 기록해도 상관이 없다!!!! 사실 파일에 쓰는 것은 byte에 쓰여진 이진수들일 뿐이니까 아무 상관이 없어야 하는게 맞다.

그래서 cp949로 된 문자열을 기록하면 그 문서는 cp949로 인코딩해서 열어야 제대로 보이고, UTF-8 이진문자를 기록한 파일은 UTF-8로 열어야 한다. Editplus같은 좋은 에디터들은 알아서 인코딩도 정해주니 얼마나 좋은가!


하지만 이렇게 파일을 열어서 쓰면 유니코드를 기록할 수 없다. 당연하지... 유니코드는 "문자열"일 뿐 어떤 이진문자로 표현될지 정해지지 않은 날 것 그대로이다. 그래서 문자열 셋을 정해서 인코딩 후 기록해야 한다.


혹은 이렇게 파일을 열 수 있는데, 

>>> f = codecs.open("codecs.txt", 'w', 'utf-8')

이 경우 맨 위에 import codecs를 해주어야 한다. 아무튼, 이렇게 파일을 열면 무조건 이 파일은 UTF-8로 작성이 된다. 그럼...?? 여기선 UTF-8로 인코딩된 문자열을 쓸 수 있어야 하지 않을까?

>>> f.write(a) ----> a는 UTF-8로 인코딩된 문자열

이러면 UnicodeDecodeError가 뜬다. -.-?????

사실 자세히 찾아볼 여유는 없었는데, codecs로 열면 반드시 작성할 때의 문자열은 유니코드여야 한다. 그래서 어떤 문자열 셋으로 변환된 문자들도 codecs를 통해 열게 된 파일에 쓰기가 안된다. 


사실 이런 에러들 때문에 python을 가끔 쓰다가 멘붕에 빠지기도 한다.


6. 서두에 붙이는  #-*- coding: utf-8 -*- 이건 뭥미?

가끔 한글 인코딩 이렇게 구글링을 하다보면 위의 문자열 셋 선언부를 보게 된다. 이걸 하고나서 누군가는 잘 되었다는데 나는 잘 안되고.... 하면서 멘붕이 지구 맨틀까지 닿을듯 몰려오는 떄가 있다.


저 선언부는 이 문서에 써져있는 한글이나 다른 문자열들이 utf-8로 쓰일 것이라는걸 미리 알려주는 것 뿐이다. 그래서 파일을 읽고 문자열을 처리하다가 생기는 에러는 위의 선언으로 고쳐질 수 없다.



새롭게 알게되는 내용이 있다면 또 포스팅해야겠다.


참고할만한 한글 문서들!!!

http://kldp.org/node/133996

http://coreapython.hosting.paran.com/boodebr/All%20About%20Python%20and%20Unicode%20%20boodebr_org.htm

http://lakhos.egloos.com/2899553

http://seorenn.blogspot.kr/2011/02/python-unicodeutf-8.html

http://www.i-fam.net/water/93


사실 구글링 잘 하면 맨 위에 나오는 문서들인데... 생각보다 다시 찾기는 쉽지 않다. ㅎㅎ


'Study > Computer' 카테고리의 다른 글

networkx에서 gexf 사용하기  (0) 2013.10.15
MS Word가 갑자기 비정상적으로 종료될 때  (0) 2013.10.01
networkx에서 link의 방향성  (0) 2013.09.19
연속된 숫자 뭉치를 뽑아내기.  (1) 2013.09.04
Amazon EC2 사용하기  (0) 2013.08.27

+ Recent posts