Click here to load reader
Upload
daesung7kang
View
3.058
Download
5
Embed Size (px)
Citation preview
강 대 성[email protected]
Character Encoding in Python
발표자 소개 - 강대성컴퓨터 전공, 경제 부전공
게임회사 3년 - MMORPG 서버 개발통신회사 2년 - 인사 관리 시스템 금융회사 6년 - 해외선물, FX거래 시스템
록앤올(김기사) 2년 - 교통정보 분석, 미래 속도 예측피플펀드컴퍼니 2015.09 ~ - 기술총괄
현)대한아이스하키협회 심판 2007~
발표 내용
Encoding의 기초UTF-8 UTF-16
ASCII EUC_KR
고통을 해결한 사례
고통과 멀어지는 팁
Encoding 에 익숙해지기
다음 중 3가지 이상을 알고 있다면...?
U+Unicode != UTF-8
BOMUTF-16LE(BE)
Two string types in python ‘unicode’ ‘str’
독일 쇼핑몰에서 주문을 하고, 배송을 시작할 때 쯤 메일왔습니다.
독일어라 알아보진 못하지만, 글자가 깨졌습니다.
왜 이럴까?
약속(Encoding)을 제대로 지키지 않음.
약속을 이해하고,
Python에서 약속을 어떻게 다루는지
어려움이 생겼을 때는 어떻게하는지?
Encoding
자료 : http://parks.seoul.go.kr/template/common/park_info/park_intro.jsp?park_id=namsan&fclass_num=794&mclass_num=1230
봉수 : 전투 상황을 5가지 신호로 전달
자료 : http://ko.wikipedia.org/wiki/봉수
http://parks.seoul.go.kr/template/common/park_info/park_intro.jsp?park_id=namsan&fclass_num=794&mclass_num=1230
평상시 1
국경에나타남 2
국경근접 3
국경침범 4
전투중 5
Encoding?
전달하려는 내용을 부호화
문자나 기호들의 집합을 저장하거나 통신에 사용할 목적으로 부호화하는 방법
EncodingCharacter
자료 : http://ko.wikipedia.org/wiki/문자_인코딩
저장, 통신을 하기 위해서 2진수가 기본
1101 0010 1010 0001
편의상 2진수 4개를 하나로 합쳐 16진수로 표현
0xD2A1
저장장치에 ‘PyCon’를 0x50 79 43 6F 6E 로 저장
Network으로 ‘강대성’을 0xEA B0 95 EB 8C 80 EC 84 B1으로 전송(UTF-8)
0xB0 AD B0 EB BC BA으로 전송 (EUC-KR)
Example
Morse Code (모스 부호, 1844) -••---•-•
FM 라디오 방송
Unicode
EUC_KR
봉수
Character Encoding 이 맞는 것은?
Character Encoding
ASCII, EBCDIC ... ASCII : American Standard Code for Information Interchange
EBCDIC : Extended Binary Coded Decimal Interchange Code
EUC_KR, CP949 ... EUC : Extended Unix Code, CP : Code Page
Unicode, UTF-8, UTF-16, UTF-32 … Unicode는 인코딩이 아님
http://b.mytears.org/2005/01/101
Encoding에 따른 동일한 문자 표현 방법
H i 똠 방 각 하
ASCII 0x48 0x69
EBCDIC 0xC8 0x89
EUC_KR 0x48 0x690xA4D4A4A8A4C7A4B1채움/ㄸ/ㅗ/ㅁ
0xB9E6 0xB0A2 0xC7CF
CP949 0x48 0x69 0x8C63 0xB9E6 0xB0A2 0xC7CF
Unicode U+48 U+69 U+B620 U+BC29 U+AC01 U+D558
UTF-8 0x48 0x69 0xEB98A0 0xEBB0A9 0xEAB081 0xED9598
UTF-16BE 0x0048 0x0069 0xB620 0xBC29 0xAC01 0xD558
UTF-16LE 0x4800 0x6900 0x20B6 0x29BC 0x01AC 0x58D5
같은 문자라도 인코딩에 따라
다르게 부호화 되고,
일부는 표현이 불가능하다.
동일한 내용을 Encoding에 따라 해석 0x50 0x79 0x43 0x6F 0x6E 0x2E 0x4B 0x52
ASCII P y C o n . K R
UTF-8 P y C o n . K R
UTF-16BE 偹 䍯 渮 䭒
UTF-16LE 祐 潃 (not valid) 剋
부호를 어떤 인코딩으로
해석을 했냐에 따라 다르게 해석된다.
UTF-8
ASCII
CP949
EUC-KR
ASCII
Unicode
Unicode전세계 문자, 기호를 Codepoint에 매칭. 한글, 타이문자
많이 쓰이는 이모티콘도 정의 ex) Pile of Poo(U+1F4A9, )
CodePoint : U+로 표현. ASCII영역은 U+0000~U+007F 한글 : 초성/중성/종성, 자음/모음, 완성글자 영역이 있음.
Unicode != UTF-8 U+AC15 != 0xEA B0 90
Unicode : 문자를 CodePoint에 매칭시킴 일반적으로 전송&저장을 위해 사용하지 않음. UTF-8 : Unicode의 전송&저장을 위한 인코딩 방법
왜 UTF-8?1. 모든 Unicode Codepoint 를 다룰 수 있다.
2. Unicode를 Encoding했을 때 NULL 이 포함되지 않음. C에서 strcpy()를 사용할 수 있음.
3. ASCII text는 UTF-8 text가 될 수 있음.
4. 주요한 문자들은 1~2bytes로 표현가능. (한글은 3 bytes)
5. 일부 바이트가 유실되어도, 다음 시작 byte를 알아낼 수 있다. https://docs.python.org/3/howto/unicode.html
Web Encoding
UTF-8 : 84.0%ISO-8859-1(서유럽어) : 8.1%
http://w3techs.com/technologies/overview/character_encoding/allhttp://ko.wikipedia.org/wiki/ISO/IEC_8859
Encoding in HTTP, HTML, E-mail ... HTTP Header : Content-Type:text/html; charset=utf-8
HTML Header : <head><title>파이콘 한국 2015</title> <meta charset="utf-8"> </head>
HTML Header : <head><meta http-equiv="Content-Type" content="text/html; charset=euc-kr" />
</head>
E-mail : Content-Type: text/plain; charset=UTF-8Content-Transfer-Encoding: base64
이제 Python 이야기Character Encoding in Python
Two string types in Python 2/3
Python2 / str : 'abc가나다' unicode : u'abc가나다'
Python3 / bytes : b'abc가나다’ str : 'abc가나다'
Two string types in Python 2/3Python 2
>>> str1 = 'PyCon.KR 2015 강대성'>>> type(str1)<type 'str'>>>> len(str1)23
>>> uni_str1 = u'PyCon.KR 2015 강대성'>>> type(uni_str1)<type 'unicode'>>>> len(uni_str1)17
Two string types in Python 2/3Python 3
>>> bytes1 = b'PyCon.KR 2015 \xea\xb0\x95\xeb\x8c\x80\xec\x84\xb1'>>> type(bytes1)<type 'bytes'>>>> len(bytes1)23
>>> str1 = 'PyCon.KR 2015 강대성'>>> type(str1)<type 'str'>>>> len(str1)17
Encode / Decode 용어 익히기
Unicode로 표현된 데이터‘가나다라abcd’
ex)2 / unicode, u’PyCon \uac15’3 / str, ’PyCon \uac15’
저장, 통신이 가능한 데이터10101101110010101010101
ex)2/str, ’PyCon \xea\xb0\x95’3/bytes, b’PyCon \xea\xb0\x95’
Decode
Encode
unicode ⇔ str in python2 unicode.encode() => str str.decode() => unicode
>>> unistr = u'PyCon.KR 2015 강대성' >>> unistr u'PyCon.KR 2015 \uac15\ub300\uc131'
>>> unistr.encode('UTF-8')'PyCon.KR 2015 \xea\xb0\x95\xeb\x8c\x80\xec\x84\xb1'
>>> unistr.encode('EUC-KR')'PyCon.KR 2015 \xb0\xad\xb4\xeb\xbc\xba'
unicode ⇔ str in python2 unicode.encode() => str str.decode() => unicode
>>> str1 = 'PyCon.KR 2015 강대성' >>> str1 'PyCon.KR 2015 \xea\xb0\x95\xeb\x8c\x80\xec\x84\xb1'
>>> str1.decode('UTF-8')u'PyCon.KR 2015 \uac15\ub300\uc131'
str ⇔ bytes in python3
str.encode() => bytes bytes.decode() => str
>>> str1 = 'PyCon.KR 2015 강대성' >>> str1 'PyCon.KR 2015 강대성'
>>> str1.encode('UTF-8')b'PyCon.KR 2015 \xea\xb0\x95\xeb\x8c\x80\xec\x84\xb1'>>> str1.encode('EUC-KR')b'PyCon.KR 2015 \xb0\xad\xb4\xeb\xbc\xba'
str ⇔ bytes in python3
str.encode() => bytes bytes.decode() => str
>>> bytes1 = b'PyCon.KR 2015 \xea\xb0\x95\xeb\x8c\x80\xec\x84\xb1' >>> bytes1 'PyCon.KR 2015 \xea\xb0\x95\xeb\x8c\x80\xec\x84\xb1'
>>> bytes1.decode('UTF-8')'PyCon.KR 2015 강대성'
간단한 연습유럽 여행을 함께 하기로 한 친구 4명이 각각 좋아하는 여행지와 소요 예산을 text 문서로 저에게 전달해주었습니다. 이것을 하나의 파일로 합치면서 정렬하기 1.csv 나폴리, 3일, EUR 500 2.csv München, 2Days, €400 3.csv 로마, 4일, 300유로 4.csv London, 3D, £300
Python3 Code
filenames = ['1.csv','2.csv','3.csv','4.csv']
datas = []for filename in filenames: f = open( filename, mode='rb' ) datas.append( f.read() ) f.close()datas.sort()f_write = open( 'summary.csv', mode='wb' )for data in datas: f_write.write(data)f_write.close()
pi@raspberrypi ~/pycon $ cat summary.csvLondon, 3D, ▒300M▒nchen, 2Days, ▒400▒θ▒, 4▒▒, 300▒▒▒나폴리, 3일, EUR 500
다 깨졌다!
Sort도 정상이지 않음
원인 : 인코딩을 고려하지 않음
1.csv 나폴리, 3일, EUR 5002.csv München, 2Days, €4003.csv 로마, 4일, 300유로4.csv London, 3D, £300
( UTF-8 ) ( ISO-8859-15 ) ( EUC-KR ) ( ISO-8859-1 )
Tip #1 : Unicode Sandwich
Unicode(In Python)
Byte( Storage, Network )
Byte( Storage, Network )
files = [('1.csv', 'UTF-8'),('2.csv','ISO-8859-15'),('3.csv','EUC-KR'),('4.csv','ISO-8859-1')]
f_write = open( 'summary.csv', mode='wb' )datas_unicode = []for file in files: f = open( file[0], mode='rb' ) data_r = f.read() data_decoded = data_r.decode( file[1] ) datas_unicode.append( data_decoded )
datas_unicode.sort()
for data_unicode in datas_unicode: data_w = data_unicode.encode('utf-8') f_write.write( data_w ) f.close()f_write.close()
Byte
Byte
Unicode
pi@raspberrypi ~/pycon $ cat summary.csvLondon, 3D, £300München, 2Days, €400나폴리, 3일, EUR 500로마, 4일, 300유로
Tip #1 : Unicode Sandwich(remind)
Unicodes(In Python)
Bytes( Storage, Network )
Bytes( Storage, Network )
인코딩 문제를 해결한 사례들...
소스코드에 한글을... ?
- Python Interpreter는 소스코드(*.py)를 입력으로 받는 프로그램
- 입력되는 소스코드에 Encoding이 명시되어 있어야 함.
Source Code Encoding
Python3
- # -*- coding: encoding -*-
- Default : UTF-8
https://docs.python.org/3/tutorial/interpreter.html#source-code-encoding
Python2- UTF-8 BOM : UTF-8
- # -*- coding: encoding -*-
- Default : ASCII
https://docs.python.org/2/library/codecs.html#standard-encodings
https://docs.python.org/2/tutorial/interpreter.html#source-code-encoding
BOM(Byte Order Mark)Unicode Codepoint : U+FEFF UTF-8 : EF BB BF ( ISO-8859-1 :  ) UTF-16 BE(Big Endian) : FE FF UTF-16 LE(Little Endian) : FF FE
http://en.wikipedia.org/wiki/Byte_order_mark
Standard I/O도 Encoding과 함께
Standard Output Encoding in Python2> cat encodingtest.py# -*- coding: UTF-8 -*-import sys
print 'Standard output encoding is', sys.stdout.encodingprint u'Test #1 ', u'PyCon 2015'print u'Test #2 ', u'강대성'
> python encodingtest.pyStandard output encoding is UTF-8Test #1 PyCon 2015Test #2 강대성
Encoding is UTF-8 = Terminal Encoding
> python encodingtest.py > output.txtTraceback (most recent call last): File "encodingtest.py", line 6, in <module> print u'Test #2 ', u'강대성'UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
> cat output.txtStandard output encoding is NoneTest #1 PyCon 2015Test #2
Redirect stdout
Encoding is None = sys.stdout.encoding
Process를 실행하면 3개의 파일이 open 됨0(Standard Input), 1(Standard Output), 2(Standard Error)
run “python”pi@raspberrypi /proc/11568/fd $ ls -llrwx------ 1 pi pi 64 Aug 23 15:46 0 -> /dev/pts/1lrwx------ 1 pi pi 64 Aug 23 15:46 1 -> /dev/pts/1lrwx------ 1 pi pi 64 Aug 23 15:46 2 -> /dev/pts/1
/proc/processnumber/fdLinux 시스템에서 각 프로세스들의 열린 파일들을 볼 수 있음.
Redirect 하면...?
run “python > test.output”pi@raspberrypi /proc/11588/fd $ ls -llrwx------ 1 pi pi 64 Aug 23 15:56 0 -> /dev/pts/1l-wx------ 1 pi pi 64 Aug 23 15:56 1 -> /home/pi/test.outputlrwx------ 1 pi pi 64 Aug 23 15:56 2 -> /dev/pts/1
output을 redirect하면
standard output 이 /home/pi/test.output으로 지정됨.
터미널 vs 파일
print 'Standard output encoding is', sys.stdout.encoding
터미널 : Standard output encoding is UTF-8Standard output encoding is EUC-KR
파일 :Standard output encoding is None
Python은 file의 Encoding을 알지 못함.
애매하지 않게 정해 주어야 함
Solution #1# -*- coding: utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8')
Solution #2import sys import codecs sys.stdout = codecs.getwriter('utf8')(sys.stdout)
일본어 디코딩 이야기
일본시스템 HTTP를 이용한 파일 전송 Python
Java/JDBC
시스템 구조
jpype미지의 세계
설명에 따르면...Encoding 을 알려주지 않고.
83년버전 JIS 한자 코드와 76년 버전 JIS 코드 혼재한자 문자열 중에 1byte계 문자가 혼재함(이런 설명이 쭉 이어짐…)
MS 일본에 있는 친구에게 물어봐도 모르겠다고 함.
일단 알고 있는 모든 Encoding을 시도print 'Detect :', chardet.detect(readdata)print 'Received data :', readdata, ':'.join(x.encode('hex') for x readdataprintENCODINGS = ['ISO-2022-JP', 'UTF-8', 'EUC-JP', 'SJIS']for ENCODING in ENCODINGS: print ENCODING, codecs.decode( readdata, ENCODING )
Detect : {'confidence': 1.0, 'encoding': 'ascii'} Received data : 5\:j@>#I#C 35:5c:3a:6a:40:3e:23:49:23:43
ISO-2022-JP : 5\:j@>#I#CUTF-8 : 5\:j@>#I#CEUC-JP : 5\:j@>#I#CSJIS : 5\:j@>#I#C
디코딩이 제대로 안되었다.
Tip #2 : 인코딩 파악
주고 받을 인코딩을 정확히 파악!
정의된 문서도 보고,주고 받은 내용을 직접 보고,
테스트!
인코딩에서 어려움을 가지는 사람들...
“ Encode, Decode 이렇게 저렇게 해보다가, 잘 되면 그것을 써요. ”
망하는 지름길
bytes(python2/str)로 입력str(python3/unicode)로 decode
with 정확한 encoding
일단 원본 데이터를 보자 print 'Received data :', readdata, ':'.join(x.encode('hex') for x readdata Received data : 5\:j@>#I#C 35:5c:3a:6a:40:3e:23:49:23:43
35 - 0011 0101 5c - 0101 1100 3a - 0011 1010 6a - 0110 1010 40 - 0100 0000
7bit encoding 인가?
http://web.archive.org/web/20060527013315/http://www.cs.mcgill.ca/~aelias4/encodings.html#section2.1.2
ISO-2022-JP
ESC $ B 를 추가.
ENCODING = 'ISO-2022-JP'ESCseq = ‘\x1b$B’print ENCODING, codecs.decode( ESCseq + readdata, ENCODING )
결과
ISO-2022-JP 宮崎西IC
성공
인코딩 파악을 위한 순서
1. 문서 또는 서로의 약속을 확인
2. 전송 받은 데이터를 열어서 확인 ( 다음장에서 상세 설명 )
3. 테스트
인코딩을 확인 & 파악 vi ( :r !xxd filename ) or 16진수 지원 Editor ':'.join(x.encode('hex') for x readdata example) 50 79 43 6F 6E EA B0 95
EB 8C 80 EC 84 B1 UTF-8 느낌
인코딩 파악에 도움이 될만한 것
UTF-8 : Paris, 3일, EUR 500ISO-8859-15 : München, 2Days, €400EUC-KR : 로마, 4일, 300유로ISO-8859-1 : London, 3D, £300
print chardet.detect(readdata)
{'confidence': 0.938125, 'encoding': 'utf-8'}{'confidence': 0.6450620150892906, 'encoding': 'ISO-8859-2'}{'confidence': 0.99, 'encoding': 'EUC-KR'}{'confidence': 0.7388892172840966, 'encoding': 'ISO-8859-2'}
테스트
전체 프로그램으로 테스트 하면 시간이 오래 걸릴 수 있으니, 해당 부분만 따로 떼어서 테스트 해본다.
ENCODING = 'ISO-2022-JP'ESCseq = ‘\x1b$B’print ENCODING, codecs.decode( ESCseq + readdata, ENCODING )
결과
ISO-2022-JP 宮崎西IC
Python에서 만든 데이터를다른 프로그램에서 못 읽어요.
대학교 때부터 함께 놀던 친구들 모임
단체 대화방에서 어느날 .... “Python에서 생성한 csv가 mac용 excel에서 열리지 않는다.. ”
직급은 한글 2자리
ASCII영역문자들은 잘 보임
직급이 6개의 무엇인가로 구성됨.
vi :r !xxd result.csv
유추 : UTF-8 Encoding 으로 된 것 같네…
MS Excel for Mac 2011에서 UTF-8 가능?
http://stackoverflow.com/questions/6588068/which-encoding-opens-csv-files-correctly-with-excel-on-both-mac-and-windows
이게 가장 좋겠다.UTF-16LE with BOM
Excel for Mac 에 대한 자료를 검색...
친구의 소스 (Python2)# -*- coding: utf-8 -*-
f = open(target_path, 'w')f.write("uidx, id, phone, name, 회사, 직장번호, 직급\n")f.close()
바꾸어 준 소스 (Python2)# -*- coding: utf-8 -*-
import codecs
f = codecs.open('testfile.txt', encoding='UTF-16LE', mode='w')f.write(u"\ufeff" ) # BOMf.write(u"uidx\t id\t phone\t name\t 회사\t 직장번호\t 직급\n")f.close()
결과
‘\ud68c\uc0ac’ (회사)
파일 IO를 위한 TIP
파일을 열 때 codecs 쓰면 간단해짐. fp = codecs.open('testfile.txt', encoding='UTF-16LE', ...)
Python3에는 내장함수에서 가능 fp = open( encoding = ‘UTF-8’, ... )
출력가능한 ASCII는 그대로나머지는 8bit를 “=XX”으로 표시
0xC3BCCP1252 = ü UTF-8 = ü표시하려고 했던 단어 für하지만 für
0xEFBBBF이건 UTF-8 BOM 같은데...
정리 : 이것만은 기억하세요.
Tip #1 : Unicode Sandwich Python에선 항상 Unicode
Tip #2 : 인코딩 파악하기 문서보고, 확인하고, 테스트 주어야하는 인코딩도 명확히
좋은 개발 문화직원의 성장합당한 보상
감사합니다A/S 보장 !!인코딩에 관련된 궁금한 사항이 있다면?스터디/모임에서 Encoding 에 관련된 세미나가 필요하다면?
mailto : [email protected]