티스토리 뷰

" 이 포스트는 이런 분에게 추천합니다. "

- 파이썬에서 한글 인코딩 문제로 골머리 않고 계시는분
UnicodeDecodeError: 'ascii' codec can't decode byte 0xbe in position 0: ordinal not in range(128) 오류를 자주 출력하는 오류에 짜증이 치밀어오르시는분
- 유니코드 , UTF-8, CP949등 인코딩에 대해서 헷갈리시는 분들
- 한글만 출력할려면 이상한 특수문자만 눈에 보시는 분들

요즘 파이썬으로 개발할일이 많아져서 계속 부딪치는게 한글 인코딩이다. 이놈의 한글 인코딩은 심심하면 이런 메세지를 보게 된다.

직역하면 유니코드 디코드 에러가 어쩌구저쩌구, 아스키 코텍이 어쩌고저쩌구.
결론부터 말하자면 0xbe 값이 아스키 범위(0~128)를 넘어섰다는 얘기다.

1) 인코딩이란?
encode의 사전적 뜻은 1. 암호로 바꾸다   2. 부호화하다   3. (외국어로) 표현하다,  라는 뜻이다. 1번 뜻은 오히려 암호학에 더 가까운 뜻이다. 사실 암호학이다. 컴퓨터가 알기 쉽게 만들어주는 언어이기 때문이다.
예를 들어 문자 인코딩은 우리가 쓰는 'A'라는 언어를 컴퓨터가 인식하기 쉬운 언어 '65'로 바꿔주는 것을 의미한다

2) 문자 코드
문자 코드는 쉽게 생각하면 아스키코드가 대표적인 예이다. 우리가 잘 외우고 있는 'A'는 아스키 10진수로 65이다. 대학교 C 시간의 첫번째 필기문제로 나올뻡한 문제이다.

이놈의 문자 코드가 여러개인게 문제다. 대표적인 문자코드를 한번 살펴보자.

(1) ASCII 코드
American Standard Code for Information Interchange, 미국 정보 교환 표준 부호
- 7bit 글자 인코딩으로 0부터 127까지 총 128개의 문자 표현이 가능

대학교때 많이 배운다 숫자, 영어는 1byte, 한글은 2byte, 주입식 교육으로 후배들한테 심심하면 물어봤던 것이 생각난다. 미국 정보 교환 표준 부호라는 거창한 설명 보다는, 아스키코드로 숫자, 영문자, 특수문자를 표현할 수 있다고 보면 되겠다. (필자는 심심하면 아스키코드표에 있는걸로 계산하기 일수이다.)Python에서 생각보다 아스키코드를 가지고 놀수 있는 경우를 많이 볼 수 있다.  아스키코드는 아래와 같이 변경할 수 있다

1
2
3
4
5
6
7
# 문자 아스키코드로 바꾸기
 
print ord('A')
 
# 아스키코드 문자로 바꾸기
 
print char(90)
cs


(2) 유니 코드(Unicode)
전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준(출처 : 위키백과)
- 한글자당 16bit(2byte) 사용
- 65536 글자내에 모든 언어가 표현이 가능함.

범용적인 코드가 유니코드이다. 파이썬에서 변수에 한글을 넣어줄때 반드시 아래와 같은 코드로 설정해줘야 한다. 아니면 인코딩 변환과정에서 UnicodeDecodeError: 'ascii' codec can't decode 라는 성가신 에러를 보게 될 것이다.

1
2
3
4
5
6
7
# 변수에 유니코드 입력하기
abc = u"안녕하세요"
print abc
 
#  유니코드 에러 나는 경우                                                                              
abc = "안녕하세요"
unicode(abc)
cs

(3) UTF-8
- 유니코드를 위한 가변 길이 문자 인코딩 방식 중 하나로, 켄 톰프슨과 롭 파이크가 만들었다. UTF-8은 Universal Coded Character Set + Transformation Format – 8-bit 의 약자이다.(출처 : 위키백과)
- 한글자당 1~4byte를 사용한다.
- ASP.NET은 물론 오늘날 웹사이트에서 범용적으로 사용하고 있다. 
- 웹 사이트에 한글 언어팩이 없더라도 한글을 표현할 수 있다.
- 조합형 방식이다. 한글의 초성, 중성, 종성등 한글의 조합 방식을 그대로 이용하고 있다. 그래서 초성, 중성, 종성 3 Byte이다.
- UTF-8로 인코딩을 하기 위해서는 반드시 변수 type이 Unicode여야 한다

위의 코드는 한글(유니코드)를 UTF-8로 인코딩한 모습니다.

1번째 줄 : "안녕하세요"를 유니코드로 문자열 저장
2번째 줄 : "안녕하세요"를 UTF-8로 인코딩
3,4번째 줄
"안녕하세요" UTF-8로 인코딩했을 경우 문자     
             >> '\xec\x95\x88\xeb\x85\x95\xed\x95\x98\xec\x84\xb8\xec\x9a\x94'
5,6번째 줄 : UTF-8이 한글을 3byte로 표현할 수 있다고해서 "\xec\x95\x88" 출력했더니 "안"이라는 문자열을 표시한 모습
7,8번째 줄 : 아까 앞에서 UTF-8 방식이 조합형 문자라고 말해서 호기심으로 앞에 "\xec"를 출력하면 'ㅇ'이 출력될것이라고 생각한 멍청한 1인.
9,10번째 줄 : 다시 UTF-8로 디코딩했더니 유니코드가 어떻게 저장되어 있는지 확인 가능

(4) CP949, euc-kr
- cp949는 인코딩은 euc-kr의 확장 및 하위호환이다.
- UTF-8이 조합형 방식이라면, cp949는 완성형 방식이다.(ex, 가, 갸, 거,겨 형태로 표현)
- CP949로 인코딩을 하기 위해서는 반드시 변수 type이 Unicode여야 한다

위의 코드는 한글(유니코드)를 CP949로 인코딩한 모습니다.

3) 다양한 트러블 슈팅으로 파이썬 한글 사용 마스터하기

필자가 겪은 다양한 한글 인코딩 에러 사례를 통해 한글 인코딩을 해결하자. 
1) 무조건 코드를 짤때 변수에 한글을 집어넣을때는 u"한글"을 이용하자.

1
2
3
# 변수에 한글 값 넣어주기
 
abc = u"안녕하세요"
cs


변수 뿐만 아니라 아래 예제와 같이 파일 다이얼로그 이용할떄, 파일 형식값 편집할 때, 메세지 박스 편집할때 다양하게 유니코드를 설정해줘야지 한글 깨짐 현상을 예방할 수 있다. 아래는 위에서 설명한 한글을 사용해야 될 경우의 예시코드를 일부 가져다놓았다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 파일 다이얼로그 이용할때 파일 형식 사용
    def FindExcelFile(self):
        edlg = QFileDialog()
        edlg.setFileMode(QFileDialog.AnyFile)
        edlg.setFilter(u"엑셀 파일 (*.xls *.xlsx *xlsm *xlsb)")         
 
        if edlg.exec_():
         self.excelfilename = edlg.selectedFiles()
         self.Edit_findExcelFile.setText(self.excelfilename[0])
 
# QMessageBox 알림 메세지 세팅
    if checklen ==0:
        QMessageBox.about(self, u"오류", u"중복된 파일 추가입니다.")
    else:           
         QMessageBox.about(self, u"알림", u"파일 %d개가 추가되었습니다." % (checklen))
cs

2) 윈도우 또는 리눅스쉘 커맨드창에서 실행시켰을 때 SyntaxError: Non-ASCII character '\xeb' in file test.py on line 5, but no encoding declared; 와 같은 에러가 보일때
이럴때 사용하는것이 코드 앞에 붙여주는 coding:cp949, coding:utf-8를 붙여주게 되면 커맨드창에서 한글이 정상적으로 보여진다.

1
2
3
4
5
# -*- coding: cp949 -*-
from bs4 import BeautifulSoup
 
# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup
cs


3) 현재 내가 받아온 string 값이 유니코드인지 utf-8인지 cp949인지 모를때
코딩은 머리가 이해하고 있더라고 결과값이 안나오면 짜증나기 마련이다. 이럴때는 무조건 디버깅 또는 print로 출력출력 밖에 없다.
에러를 이해하기 쉬운 가장 좋은 예제는 다음과 같다.

 

1번째 줄 : "안녕하세요"를 유니코드로 저장
2,3번째 줄 : s 변수의 type을 확인해 봤더니 "유니코드"이다.
4~9번째 줄 : s 변수를 각각 UTF-8, CP949로 인코딩하였더니 type은 'str(아스키 코드)'이다.
10번째 줄 : UTF-8로 디코딩했더니 다시 type이 'unicode'로 변경되었다.

이렇게 type 함수를 통해 현재 내가 변수에 저장한 문자열이 str(아스키코드)인지 unicode인지 확인이 가능하다.
자 만약에 특정 변수를 아래와 같이 unicode 함수에 인자로 넣고 호출하였는데 아래와 같은 에러가 발생했을 경우
>>> unicode(s3)
>>> UnicodeDecodeError: 'ascii' codec can't decode byte 0xbe in position 0: ordinal not in range(128)

1. 만약 위와 같은 에러가 발생했을 때는 우선 s3 변수를 UTF-8 또는 CP949로 먼저 decoding을 해보자.
2. 그 다음에 print type(s3)로 했을 경우 unicode로 출력되는지 확인하자.

대부분 이 방법을 이용하면 UnicodeDecodeError는 어디서 문제가 발생했는지 인지하고 해결이 가능할 것이다.

4) 실전 코드 활용하기.
이전 "파이썬을 통애 나라장터 파싱 프로그램" 포스트에서 아래와 같이 유니코드와, CP949를 인코딩/디코딩하는 코드가 있었다.
필자는 이 유니코드 디코드 에러 때문에 상당한 스트레스를 받았던걸로 기억한다. 이중에서 중요한 몇 줄만 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# -*- coding: cp949 -*-
from bs4 import BeautifulSoup
 
soup = BeautifulSoup(body,'html.parser')
 
parse_a = soup.find_all('a')
parse_tr = soup.find_all('tr')
 
#####################################################################
#1 : 공고명 parsing
for test in parse_tr:
    itemstring = test.encode('cp949')
    start = itemstring.find("<td class=\"tl\"><div><a")
    if start >0:
        nlast = itemstring.find("</a></div></td>", start)
        tmpstring = itemstring[start:nlast]
        nfirst = tmpstring.rfind("\">")+2
        item = tmpstring[nfirst:]
        try:
            string = item.decode('cp949')
            worksheet.write(name_count,2, unicode(string))
        except UnicodeDecodeError:
            ErorCheck =1
            worksheet.write(name_count,2, u"오류", xlwt.easyxf("font: bold on;pattern: pattern solid, fore_color pink;"))
        
        name_count = name_count+1
cs

1, 2번째 줄 : 웹 페이지 파싱을 통해 데이터를 읽어와서 변수에 집어넣었더니 한글 깨짐 현상이 발생하였다 . 따라서 CP949로 문자열을 인코딩하였다.
20, 21번째 줄 : 엑셀에 한글 데이터를 집어넣을려면 unicode 방식을 이용해야 된다. 따라서 CP949로 다시 디코딩 해주는 모습이다.

5) 참고 사이트(Reference)

- 인코딩 방식 이해하기 (http://studyforus.tistory.com/167)
- 문자 인코딩의 개념(http://croak.tistory.com/44)


이 글을 통해 Python 한글 인코딩 에러 해결에 도움이 됬으면 하는 바람이다.









댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday