도비LOG(跳飛錄)

도비의 AI 엔지니어 도전기

Python

[전문가를 위한 파이썬] 4장 텍스트와 바이트 내용 정리

나쁜도비 2024. 5. 19. 11:18

4.1. 문자 문제

- '문자'의 정의

문자의 단위 원소(코드 포인트)는 10진수 0에서 1,114,111까지의 숫자이며, 유니코드 표준에서는 'U+' 접두사를 붙여 4자리에서 6자리 사이의 16진수로 표현한다. 

- 인코딩: 코드 포인트를 바이트 시퀀스로 변환하는 것

- 디코딩: 바이트를 코드 포인트로 변환하는 것 

- 즉, bytes는 저장이나 전송을 위한 기계 메모리 덤프이며, 유니코드 str은 사람이 읽을 수 있는 텍스트라고 생각할 수 있다.

 

 

4.2. 바이트에 대한 기본 지식

- bytes: 불변형

- bytearray: 가변형

- bytes와 bytearray에 들어있는 각 항목은 0~255 사이의 정수.

 

4.2.1. 구조체와 메모리 뷰

- struct 모듈은 패킹된 바이트를 다양한 형의 필드로 구성된 튜플로 분석하고, 이와 반대로 튜플을 패킹된 바이트로 변환하는 함수를 제공한다.

 

4.3. 기본 인코더/디코더

- 파이썬에는 텍스트와 바이트 간 변환을 위한 100여 개의 코덱이 포함되어 있다. (utf-8 등)

 

4.4. 인코딩/디코딩 문제 이해하기

4.4.1. UnicodeEncodeError 처리하기

- 대부분의 비 UTF 코덱은 유니코드 문자의 일부만 처리할 수 있다. 문자가 대상 인코딩에 정의되어 있지 않으면, UnicodeEncodeError가 발생한다.

 

4.4.2. UnicodeDecodeError 처리하기

- 모든 바이트 시퀀스가 정당한 UTF-8이나 UTF-16 문자가 되는 것은 아니다. 따라서, 이진 시퀀스를 텍스트로 변환할 때 정당한 문자로 변환할 수 없다면 UnicodeDecodeError가 발생한다.

 

4.4.3. 예상과 달리 인코딩된 모듈을 로딩할 때 발생하는 SyntaxError

- 파이썬 3에서 인코딩 선언 없이 비 UTF-8로 인코딩된 .py 모듈을 로딩하면 SyntaxError가 발생한다.

- `# coding: cp1252` 와 같은 주석을 파일 맨 꼭대기에 달아서 해결할 수 있다.

 

4.4.4. 바이트 시퀀스의 인코딩 방식을 알아내는 방법

- 바이트 시퀀스의 인코딩 방식은 알아낼 수 없으며, 반드시 별도로 인코딩 정보를 가져와야 한다.

- 다만 바이트 스트림이 자연어라고 간주되면, 규칙을 통해 Chardet 패키지로 문자 인코딩 방식을 알아낼 수 있다.

 

4.5. 텍스트 파일 다루기

- 텍스트를 처리하는 최고의 방법은 '유니코드 샌드위치'이다. 이것은 파일을 읽을 때 가능하면 빨리 bytes를 str로 변환해야 한다는 것을 의미한다. 즉, 다른 처리를 하는 도중에 인코딩이나 디코딩을 하면 안 된다. 출력할 때는 가능한 한 늦게 str을 bytes로 인코딩한다.

 

 

4.6. 제대로 비교하기 위해 유니코드 졍규화하기

- 유니코드에는 결합 문자가 있기 때문에 문자열 비교가 복잡하다.

- 눈으로 보기에 동일한 발음기호 첨자가 삽입된 문자이더라도 실제 코드 포인트는 서로 다를 수 있다.

- 이 문제 해결을 위해 unicodedata.normalize() 함수가 제공하는 유니코드 정규화를 이용해야 한다. 

    - 정규화 방식 C(NFC)는 코드 포인트를 조합해서 가장 짧은 동일 문자열을 생성한다.

    - NFD는 조합된 문자를 기본 문자와 별도의 결합 문자로 분리한다.

    - NFKC, NFKD는 '호환성 문자'에 영향을 미친다. 

 

4.6.1. 케이스 폴딩

- case folding 은 모든 텍스트를 소문자로 변환하는 연산이다. str.casefold()로 수행한다.

- str.lower() 와 str.casefold()가 서로 다른 문자를 반환하는 코드 포인트는 116개 있다.

 

4.6.2. 정규화된 텍스트 매칭을 위한 유틸리티 함수

- 다양한 언어 텍스트를 다루는 경우 아래 함수를 사용하면 좋다.

def nfc_equal(str1, str2):
	return normalize('NFC', str1) == normalize('NFC', str2)
    
def fold_equal(str1, str2):
	return (normalize('NFC', str1).casefold() == normalize('NFC'), str2).casefold())

 

4.6.3. 극단적인 '정규화': 발음 구별 기호 제거하기

- 악센트나 발음 구별 기호를 무시하는 방법도 있다. 다만, 단어의 뜻을 변경할 수도 있으므로 적절한 정규화 방식은 아닐 수 있다.

import unicodedata
import string

def shave_marks_latin(txt):
	norm_txt = unicodedata.normalize('NFD', txt) # 모든 문자를 기반 문자와 결합 표시 기호로 분리
    latin_base = False
    keepers = []
    for c in norm_txt:
    	if unicodedata.combining(c) and latin_base: # 기반 문자가 라틴 문자일 때 결합 표시 기호를 건너 뛴다.
        	continue # 라틴 기반 문자의 발음 구별 기호를 무시한다.
        keepers.append(c)
        # 결합 문자가 아니면, 이 문자를 새로운 기반 문자로 간주한다.
        if not unicodedata.combining(c): # 새로운 기반 문자를 찾아내고 라틴 문자인지 판단한다.
        	latin_base = c in string.ascii_letters
    shaved = ''.join(keepers)
    return unicodedata.normalize('NFC', shaved) # 문자들을 모두 결합하고 NFC 방식으로 정규화한다.

 

 

4.7. 유니코드 텍스트 정렬하기

- 문자열 정렬의 경우 각 단어의 코드 포인트를 비교한다. 

- 비 아스키 텍스트는 locale.strxfrm() 함수를 이용해 변환하는 것이 표준이다.

- pycua.Collator()를 이용해 비 아스키 코드 정렬을 쉽게 할 수 있다.

 

4.8. 유니코드 데이터베이스

- 유니코드 데이터베이스에는 코드 포인트를 문자명으로 매핑하는 테이블을 비롯해 각 문자에 대한 메타데이터 및 각 문자의 연관 방법을 담고 있다.

 

4.9. 이중 모드 str 및 bytes API

 

4.9.1. 정규 표현식에서의 str과 bytes

- bytes로 정규 표현식을 만들면 \d와 \w 같은 패턴은 아스키 문자만 매칭되지만, str로 이 패턴을 만들면 아스키 문자 이외의 유니코드 숫자나 문자도 매칭된다.

 

4.9.2. os 모듈 함수에서의 str과 bytes

- 실제 OS의 파일명은 어떠한 인코딩 체계에서도 올바르지 않은 바이트 시퀀스로 구성되어 있으며 str로 디코딩할 수 없다.

- 이 문제를 해결하기 위해 파일명이나 경로명을 받는 모든 os 모듈 함수는 str이나 bytes 형의 인수를 받고, 이 인수는 sys.getfilesystemencoding() 함수에 의해 지정된 코덱을 이용해서 자동으로 변환되고, 운영 체계의 응담은 동일 코덱을 이용해서 디코딩된다.

 


실습 코드: 파이썬에서의 한글 처리

https://colab.research.google.com/drive/1z8GqFYtkIUXhiDH6JOMBrLJZz4VRsGNf#scrollTo=9pwaMX-BqPz0

728x90