[출처 : http://www.i-fam.net/water/93]


오랫만에 파이썬을 사용하다 보니 이것 저것 문제가 한둘이 아니다. 손에 익지 않은 도구를 잡고 사용한다는 것이 이렇게 힘든 일인가보다. 웹사이트에서 로딩한 문자열에서 원하는 문자열을 찾는 것이 잘 되지 않아 이리저리 고민하다보니 결국 문자열 인코딩이 다르다는 것을 알았다. 특히나 XML 은 UTF-8 인코딩을 주로 사용한다. 그러다보니 한글윈도우즈에 저장되는 한글코드와는 인코딩이 달라 동일한 글자라하더라도 Binary 코드 자체가 다른 것이다. 이를 일치하게 만들어 주기 위해서는 코드 변환을 해야한다.

코드를 변환하는 방법은 의외로 간단했다. 입력된 문자들은 모두 ASCII 코드로 인식되므로 일단 본래의 인코딩으로 인식할 수 있도록 만들어 줘야한다. 이때 사용하는 함수가 unicode(string, encoding) 함수이다. 이렇게 원래의 인코딩으로 변환을 하고 나면 다른 인코딩으로 변환이 가능하다. 한예를 들어보면 다음과 같다.

hanguel = unicode('한글', 'euc-kr').encode('utf-8')

이는 기본적으로 윈도우즈에서 사용되는 euc-kr 인코딩의 한글을 utf-8 인코딩의 한글로 변환하는 것이다. 하지만 파이썬 소스 파일 자체가 UTF-8 로 저장되어 있다면 말이 달라질 수도 있다. unicode('한글', 'utf-8') 만으로도 제대로 인코딩된 한글이 만들어 질 수 있는 것이다.

웹에서 utf-8 로 로딩한 문자열을 윈도즈에서 사용하는 문자열로 바꾸기 위해서는 반대의 과정을 수행하면 된다. 
소스가 다른 문자열의 비교나 검색에 문제가 있다면 일단 인코딩을 의심해 보자.

다음은 인코딩과 관련된 내용을 잘 정리한 외국 사이트의 내용을 번역한 것이다. 대충 번역한 것이라 참조만 하기 바라며, 본문 중 애매한 내용은 원문을 참조하기 바란다.

원본링크 : http://evanjones.ca/python-utf8.html (새 창으로 열기)

기초
파이썬에는 두가지 형태의 문자열이 있다.  바이트 문자열과 유니코드 문자열이 그것이다. 여러분이 생각하는 것처럼 바이트 문자열은 바이트의 시퀀스이다. 파이썬은 여러분 컴퓨터의 기본 로케일을 사용하여 바이트를 문자들로 변환한다. Mac OS X 에서는 기본 로케일이 실제로 UTF-8 이지만 그 외 대부분의 시스템에서는 기본 로케일이 아마도 ASCII 일 것이다.
다음 코드는 바이트 문자열을 생성한다.

byteString = "hello world! (in my default locale)"

그리고 다음 코드는 유니코드 문자열을 생성한다.

unicodeString = u"hello Unicode world!"

바이트 문자열을 유니코드 문자열로 변환하고 다시 바이트 문자열로 변환하는 코드는 다음과 같다.

s = "hello byte string"
u = unicode(s)
backToBytes = u.encode()

위의 코드는 여러분의 기본 문자집합을 변환에 사용한다. 그러나 로케일의 문자열집합을 신뢰하는 것은 나쁜 생각이다. 왜냐하면 여러분의 응용프로그램을 타일랜드에서 있는 누군가가 자신의 컴퓨터에서 실행하고자 한다면 바로 멈춰 버릴 것이기 때문이다. 대부분의 경우 문자열의 인코딩을 명확하게 정의해주는 것이 훨씬 낫다.

s = "hello normal string"
u = unicode(s, "utf-8")
backToBytes = u.encode("utf-8")

이제 바이트 문자열 s 는 유니코드 문자열 u 를 생성하여 UTF-8 바이트 시퀀스로 취급할 수 있을 것이다. 다음 라인은 UTF-8 의 표현인 u 를 다시 바이트 코드 문자열인 backToBytes 로 저장한다.

유니코드 문자열로 작업하기
감사하게도 파이썬은 모든 면에서 유니코드 문자열과 바이트 문자열이 동등하게 취급된다고 가정한다. 그러나 여러분은 만약 객체가 문자열인지 보려고 테스트 할 때 여러분 자신의 코드에 주의를 기울일 필요가 있다. 이런 식으로 하지마라.

if isinstance(s, str): # 나쁜 예: 유니코드 문자열에 대해 참이 아님

대신 basestring 이라는 일반 문자열 기반 클래스를 사용하라.

if isinstance(s, basestring): # 유니코드와 바이트 문자열에 대해 모두 참

UTF-8 파일 읽기
여러분은 파일에서 읽은 문자열을 수동을 변환할 수 있다. 그러나 다음과 같은 방법을 사용하면 좀더 쉽게 할 수 있다.

import codecs
fileObj = codecs.open( "someFile", "r", "utf-8")
u = fileObj.read() # 파일에 있는 UTF-8 바이트 들이 유니코드 문자열로 넘어옴

codecs module은 여러분이 하고자하는 모든 변환에 사용될 수 있다. 여러분은 또한 저장을 위해 파일을 열 수 있고, 여러분이 write 에 전달한 유니코드 문자열을 은 여러분이 선택한 인코딩이 무엇이건 변환되어 저장될 것이다.

XML과 minidom으로 작업하기
나는 XML 관련작업에 대부분 내가 친숙한 minidom module 을 사용한다. 불행히도 XML은 단지 바이트 문자열만을 다룬다. 따라서 minidom 함수에 넘겨주기 전에 유니코드 문자열로 인코딩해야 한다. 예제는 다음과 같다.

import xml.dom.minidom
xmlData = u"<français>Comment ça va ? Très bien ?</français>"
dom = xml.dom.minidom.parseString( xmlData )

마지막 열은 다음과 같은 예외를 발생시킨다.

UnicodeEncodeError: 'ascii' codec can't encode character '\ue7' in position 5: ordinal not in range(128). 

이런 에러 피하려면 minidom으로 문자열을 전달하기 전에 적절한 포맷으로 유니코드 문자열을 인코딩해야한다. 다음과 같이 해보자.

import xml.dom.minidom
xmlData = u"<français>Comment ça va ? Très bien ?</français>"
dom = xml.dom.minidom.parseString( xmlData.encode( "utf-8" ) )

minidom은 Latin-1 또는 UTF-16 등 어떠한 바이트 문자열의 어떤 포멧이던 다룰 수 있다. 하지만 XML 문서가 인코딩선언(eg. <?xml version="1.0" encoding="Latin-1"?>)을 가지고 있어야 정상적으로 작동한다. 만약 인코딩 선언이 빠진다면 minidom 은 인코딩을 UTF-8로 가정한다. 모든 시스템에서의 호환을 보장하려면, 당신의 모든 XML 문서에 인코딩선언을 포함하는 습관을 갖는 것이 좋다. 
minidom의 dom.toxml() 또는 dom.toprettyxml() 을 호출하여 생성되는 XML 결과는 유니코드 문자열로 넘어온다. encoding="utf-8" 이라는 여분의 매개변수를 전달하여 인코딩된 바이트 문자열을 얻을 수 있다.

+ Recent posts