Python/인코딩(encoding)

파이썬 인코딩 문제 (해결함): 주요 코덱들(utf8, cp949, utf16)을 다 집어넣어 보기

공시탈출넘버원 2023. 9. 13. 16:04

한국어 처리할 때 제일 골치아픈 것은 인코딩 문제이다. 특히 UnicodeDecodeError 이거는 시도때도 없이 튀어나와서 사람 애먹인다.

 

처음에는 utf-8 에러가 뜨길래, cp949로 바꿨다. 그랬더니 다른 문서에서 에러가 뜨길래 utf-16을 써봤으나 별 도움이 안 되었다. 에러의 대표적인 예시:

'utf-8' codec can't decode byte 0xbe in position 0: invalid start byte

UTF-16 stream does not start with BOM

'cp949' codec can't decode byte 0xbf in position 2: illegal multibyte sequence

전제: (txt 파일 read 로 불러올 때)

with open (file_path, encoding = encoding_name) as x:

    text_name = x.read()

문법에서 file_path에 해당하는 텍스트계열 확장자에는 문제가 없음을 전제로 한다.

문제가 있다면 특정 확장자를 잘 불러올 수 있는 전용 라이브러리를 추가하시오.

 

다른 상황에 적용해야 하는 사람은 아래 코드나 흐름을 보고 응용하시오. 핵심은, 자주 쓰는 순서대로 계속 인코딩을 호출해가며 시도하는 것이다.

 

코드의 기능

아래의 코드는 텍스트파일이 인코딩 인식할 수 있을때까지 계속 꼬라박아보는 방식이다.

무식한데 이것만큼 효과적인 방법은 없었다.

다같은 txt파일로 저장해놨을텐데 인코딩이 중간중간 제각각이다.

막상 써보면 utf-8, cp949, utf-16 이 세개만 있어도 사실상 충분하니 빨리 돌리고 싶으면 저것만 쓰자.

 

textfile_path # 텍스트 파일의 주소. type str. 확장자는 txt 권장. 아마도 메모장으로 바로 열리는 정도면 유사 확장자여도 충분히 될것이다.

encoding_list = ['utf-8','cp949','utf-16'] # 3대 한국어 인코딩. 이거면 충분할 것이다.
# encoding_list = ['utf-8','utf-8-sig','utf8mb4','cp949','euc-kr','utf-16'] # 내가 주로 쓰던 목록. cp949가 euc-kr의 상위호환일것이나 일단 넣어놓았다.

for k in range(len(encoding_list)):
    try:
        with open(textfile_path, encoding=encoding_list[k]) as h:
            temp_text = h.read()
            h.close()
            print(encoding_list[k], 'encoding success')
		# 첫번째 인코딩부터 시작해서 하나씩 대입하여 읽어오라
    except UnicodeDecodeError as e1:
        print('UnicodeDecode Error:',e1)
        pass
        # 인코딩에 실패했는데, 유니코드_디코드_에러가 났다면 알려주고 다음 목록으로 진행
        # 아래는 대표예제
        # UnicodeDecode Error: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
    except UnicodeError as e2:
        print('UnicodeError:', e2)
        pass
        # 인코딩에 실패했는데, 유니코드_에러가 났다면 알려주고 다음 목록으로 진행
        # 정확히 뭐였는지는 기억안나는데, 무튼 다른 이유로 인식실패했다는 문구
    except:
        print('Errors: etc')
        # 대부분의 인코딩 에러가 유니코드_디코드_에러 아니면 유니코드_에러 라서 이쪽 에러면은 직접 찾아볼것.
        
    else:
        break
        # try 문이 에러없이 잘 수행되었다면, 더 할거없으니 for k loop를 종료.
'euc_kr' codec can't decode byte 0xff in position 0: illegal multibyte sequence


Unicode Decode Error 별 최적 인코딩과 주요 에러문구

최적 인코딩 주요 바이트 다른 인코딩의 에러문구
'utf-8' 0x0a 
0xbf
'cp949' 
codec can't decode byte 0xbf in position 2: illegal multibyte sequence

'utf-16-le'
codec can't decode byte 0x0a in position 34616: truncated data

UnicodeError: UTF-16 stream does not start with BOM
'utf-16' 0xff 'euc_kr', 'cp949'
codec can't decode byte 0xff in position 0: illegal multibyte sequence

'utf-8'
codec can't decode byte 0xff in position 0: invalid start byte
'euc-kr'
'cp949'
0xb0 0xb4 0xbe 0xbf
0xc0 0xc1
'utf-8' 
codec can't decode byte 0x?? in position 34: invalid start byte

'utf-16-le'

codec can't decode bytes in position 76-77: illegal UTF-16 surrogate


그 밖에 에러문구. 
#1. utf8mb4 코덱 인식불가
Errors:  unknown encoding: utf8mb4

#2: ascii 코덱 범위초과
UnicodeDecodeError: 'ascii' codec can't decode byte 0x84 in position 4: ordinal not in range(128)
아스키 코덱은 설정 자체가 0x00 ~ 0x7F(127) 까지만 인식하므로, 0x80~0xFF(128~255)에 해당하는 바이트 값이 인식되면 튕긴다.
256까지 확장된 latin-1 (https://en.wikipedia.org/wiki/ISO/IEC_8859-1) 으로 바꾸면 될거다.

 

위의 방식처럼 낱개로 찾다간 끝이 없을것 같아서, utf-8, cp949, utf-16 총3가지에 대해 16진수 4자리(== 4니블 == 2바이트)를 어떻게 인식하는지를 쭉 나열해 보았다.

주의: 유니코드와 인코딩 간의 개념이 명확하지 않을때 적은 것이라 오류가 좀 있다.

 

글자를 인식하려고 2바이트씩 부르는데, 개중에 대응하지 않는 2바이트가 있으면 Unicode Decode Error 가 난다고 보면 되겠다. 나무위키의 cp949 등의 문서를 읽어보면 조금 도움된다.

 

그 결과는 아래와 같으며, 바로 다운받거나 자기 구글드라이브에 복사해서 쓰거나 다운받는 등 여러가지 방법으로 가져갈 수 있게 해놨다. 구글 스프레드시트 열어서 바로 엑셀 다운받기 누르면 약 3분정도 걸린다. 

 

 

https://docs.google.com/spreadsheets/d/1uqwFQKfm1Dfo9er6lcG8eAmnsPg-mkVZKNQYXy0gc70/edit?usp=sharing

 

EncodingError_1~2Bytes_Test_Gsheet

main 10진수,16진수,decode: Y or N,Actual Unicode Shown,compare DEC,HEX,utf-8,cp949,utf-16(le),utf-8,cp949,utf-16(le),p == decoded, pp == decoded but had error to save it as actual letter or picture 0,0,decoded,decoded,decoded,pp,pp,pp 1,1,decoded,decod

docs.google.com

 

 

 



표 만들때 실험하거나 썼던 코드

더보기

하도 Unicode Decode Error 가 잘 나길래, 도대체 어디서 에러가 나는지 궁금해서 바이트로 저장했다가 뽑아보는 코드들도 썼다.

## 여러가지 encode decode 방법

## 1. 바이트로 저장된 값을 리스트로 변환했다, 바이트로 변환했다 디코드.
a = b'\xea\xb7\x9c\xec\xa0\x95\xed\x95\x98\xeb\x8a\x94 \xea\xb7\xb8\xeb\x9f\xac\xed\x95\x9c'
b = list(a)
c = bytes(b)
d = c.decode('utf-8')
print('결과:_',d)

## 2. 바이트로 저장된 값을, bytearray 기능으로 바꾸고 디코드
a = b'\xb1\xb2'
c = bytearray(a)
d = c.decode('cp949')
print('결과:_',d)

e = c.decode('utf-16')
print('결과:_',e)

 

스프레드시트 표를 만들때 썼던 코드.

바이트 표기를 엑셀로 만드려고, 네다섯 시간 정도 썼네

pip install openpyxl
pip install xlsxwriter

import pandas as pd
import re


en = ['utf-8','cp949','utf-16']
l = [[],[],[]]

fmt = '\\x{0:02X}\\x{1:02X}'

for k in range(3):
    for i in range(256*256):
        x1 = fmt.format(*(i.to_bytes(2, byteorder='big'))) # 바이트값 \xFF\xFF 만드려고 사용
        x2 = i.to_bytes(2,byteorder='big') # 단순 계산용
        # 나도 x1, x2로 나누어서 쓰고 싶지 않았는데 내 머리로는 안 된다.
        
        try:
            y = x2.decode(en[k]) # 이 인코딩이 먹힌다면,
            l[k].append(y) # decoded의 약자 dcdd를 리스트에 추가      
        except:
            l[k].append(x1) # 인코딩이 안 먹힌다면, byte 값(\xFF\xFF 형식)을 리스트에 추가
            
df = pd.DataFrame(l).T
df

# 아래 코드는 저장하다 에러나는데 어떻게 잘 수정해보기 바란다.
ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]')
df2 = df.applymap(lambda x: ILLEGAL_CHARACTERS_RE.sub(r'', x) if isinstance(x, str) else x)
df2.to_excel("save.xlsx")


## 그냥 기록용. 당신한테 필요없으면 아래는 모두 지워도 된다.
'''
아래의 방법은 특수문자를 저장하는 데 모두 실패했다.
원인은 그 

그냥 저장.

pip install xlsxwriter 사용.

applymap 사용
dataframe = dataframe.applymap(lambda x: x.encode('unicode_escape').
                 decode('utf-8') if isinstance(x, str) else x)
'''

 

 

코드를 만들때 핵심이 되었던 질의응답들

11번의 시도, 2번의 나름 성공을 제외한 나머지 과정에서 겪은 질의응답은 생략.

 

2바이트의 앞 또는 뒤 바이트가 ASCII로 표현되는걸 막기위한 기능

https://stackoverflow.com/questions/49687823/int-to-byte-conversion-not-representing-in-hexadecimal-in-python

 

유니코드 문자 잔뜩 들은것을, 바로 df.to_excel() 쓰면 illegal character error 가 뜨는데 그거 해결법

https://stackoverflow.com/questions/42306755/how-to-remove-illegal-characters-so-a-dataframe-can-write-to-excel