복붙노트

[PYTHON] 파이썬에서 BOM 문자로 유니 코드 파일 데이터 읽기

PYTHON

파이썬에서 BOM 문자로 유니 코드 파일 데이터 읽기

파이썬을 사용하고 유니 코드 BOM 오류로 실행되는 일련의 소스 코드 파일을 읽습니다. 내 코드는 다음과 같습니다.

bytes = min(32, os.path.getsize(filename))
raw = open(filename, 'rb').read(bytes)
result = chardet.detect(raw)
encoding = result['encoding']

infile = open(filename, mode, encoding=encoding)
data = infile.read()
infile.close()

print(data)

보시다시피, 나는 chardet을 사용하여 인코딩을 감지하고 메모리에있는 파일을 읽고 그것을 인쇄하려고합니다. print 문은 오류가있는 BOM이 포함 된 유니 코드 파일에서 실패합니다.

기본 문자 집합을 사용하여 BOM을 디코딩하려고하면 오류가 발생합니다. 어떻게 이것을 막기 위해 문자열에서 BOM을 제거합니까?

해결법

  1. ==============================

    1.BOM이 존재하는지 여부를 확인할 이유가 없습니다. UTF-8-SIG는 BOM이 없으면 utf-8과 똑같이 작동합니다.

    BOM이 존재하는지 여부를 확인할 이유가 없습니다. UTF-8-SIG는 BOM이 없으면 utf-8과 똑같이 작동합니다.

    # Standard UTF-8 without BOM
    >>> b'hello'.decode('utf-8')
    'hello'
    >>> b'hello'.decode('utf-8-sig')
    'hello'
    
    # BOM encoded UTF-8
    >>> b'\xef\xbb\xbfhello'.decode('utf-8')
    '\ufeffhello'
    >>> b'\xef\xbb\xbfhello'.decode('utf-8-sig')
    'hello'
    

    위의 예에서 utf-8-sig는 BOM의 존재 여부에 관계없이 주어진 문자열을 올바르게 디코딩하는 것을 볼 수 있습니다. BOM 문자가 읽고있는 파일에 존재할 가능성이 적다 고 생각되면 utf-8-sig를 사용하고 걱정하지 마십시오.

  2. ==============================

    2.utf-8-sig 인코딩을 명시 적으로 사용하지 않으면 UTF-16을 디코딩 할 때 BOM 문자가 자동으로 제거되지만 UTF-8은 제거되지 않아야합니다. 다음과 같이 시도해 볼 수 있습니다.

    utf-8-sig 인코딩을 명시 적으로 사용하지 않으면 UTF-16을 디코딩 할 때 BOM 문자가 자동으로 제거되지만 UTF-8은 제거되지 않아야합니다. 다음과 같이 시도해 볼 수 있습니다.

    import io
    import chardet
    import codecs
    
    bytes = min(32, os.path.getsize(filename))
    raw = open(filename, 'rb').read(bytes)
    
    if raw.startswith(codecs.BOM_UTF8):
        encoding = 'utf-8-sig'
    else:
        result = chardet.detect(raw)
        encoding = result['encoding']
    
    infile = io.open(filename, mode, encoding=encoding)
    data = infile.read()
    infile.close()
    
    print(data)
    
  3. ==============================

    3.나는 Chewie의 답을 기반으로 멋진 BOM 기반 탐지기를 구성했습니다. 데이터가 알려진 로컬 인코딩 또는 BOM이있는 유니 코드 (텍스트 편집기에서 일반적으로 생성하는 것) 중 하나 일 수있는 일반적인 경우에는 충분합니다. 더 중요한 것은, chardet과 달리 임의의 추측을하지 않으므로 예측 가능한 결과를 얻을 수 있습니다.

    나는 Chewie의 답을 기반으로 멋진 BOM 기반 탐지기를 구성했습니다. 데이터가 알려진 로컬 인코딩 또는 BOM이있는 유니 코드 (텍스트 편집기에서 일반적으로 생성하는 것) 중 하나 일 수있는 일반적인 경우에는 충분합니다. 더 중요한 것은, chardet과 달리 임의의 추측을하지 않으므로 예측 가능한 결과를 얻을 수 있습니다.

    def detect_by_bom(path,default):
        with open(path, 'rb') as f:
            raw = f.read(4)    #will read less if the file is smaller
        for enc,boms in \
                ('utf-8-sig',(codecs.BOM_UTF8,)),\
                ('utf-16',(codecs.BOM_UTF16_LE,codecs.BOM_UTF16_BE)),\
                ('utf-32',(codecs.BOM_UTF32_LE,codecs.BOM_UTF32_BE)):
            if any(raw.startswith(bom) for bom in boms): return enc
        return default
    
  4. ==============================

    4.2014 년 10 월 7 일 2.3.0 버전 출시 이후 자동으로 BOM_UTF8을 감지합니다.

    2014 년 10 월 7 일 2.3.0 버전 출시 이후 자동으로 BOM_UTF8을 감지합니다.

    #!/usr/bin/env python
    import chardet # $ pip install chardet
    
    # detect file encoding
    with open(filename, 'rb') as file:
        raw = file.read(32) # at most 32 bytes are returned
        encoding = chardet.detect(raw)['encoding']
    
    with open(filename, encoding=encoding) as file:
        text = file.read()
    print(text)
    

    참고 : Chardet은 텍스트에 BOM을 남기는 'UTF-XXLE', 'UTF-XXBE'인코딩을 반환 할 수 있습니다. 'LE', 'BE'는이를 피하기 위해 제거되어야합니다. 예를 들어, @ ivan_pozdeev의 대답과 같이이 시점에서 BOM을 쉽게 감지 할 수 있습니다.

    Windows 콘솔에 유니 코드 텍스트를 인쇄하는 동안 UnicodeEncodeError를 피하려면 Python, 유니 코드 및 Windows 콘솔을 참조하십시오.

  5. ==============================

    5.다른 답변은 너무 복잡합니다. 바이너리 파일 I / O의 하위 레벨 관용구에 빠질 필요가없는 더 간단한 방법이 있습니다. Python 표준 라이브러리의 일부가 아닌 휴리스틱 (chardet) 문자 세트에 의존하지 않으며 UTF-16 계열에서 아날로그가없는 것으로 보이는 대체 인코딩 서명 (utf-8-sig 대 일반적인 utf-8)이 필요합니다.

    다른 답변은 너무 복잡합니다. 바이너리 파일 I / O의 하위 레벨 관용구에 빠질 필요가없는 더 간단한 방법이 있습니다. Python 표준 라이브러리의 일부가 아닌 휴리스틱 (chardet) 문자 세트에 의존하지 않으며 UTF-16 계열에서 아날로그가없는 것으로 보이는 대체 인코딩 서명 (utf-8-sig 대 일반적인 utf-8)이 필요합니다.

    내가 찾은 가장 간단한 방법은 유니 코드의 BOM 문자를 처리하고 코덱을 처리하는 것입니다. 유니 코드 바이트 순서 표시가 하나뿐이므로 데이터가 유니 코드 문자로 변환되면 거기에 있는지 여부를 확인하거나 추가 / 제거하는 것이 쉽습니다. 가능한 BOM이있는 파일을 읽으려면 :

    BOM = '\ufeff'
    with open(filepath, mode='r', encoding='utf-8') as f:
        text = f.read()
        if text.startswith(BOM):
            text = text[1:]
    

    이것은 재미있는 모든 UTF 코덱 (예 : utf-8, utf-16le, utf-16be, ...)에서 작동하며 추가 모듈을 필요로하지 않으며 바이너리 파일 처리 또는 특정 코덱 상수로 떨어 뜨릴 필요가 없습니다.

    BOM을 작성하려면 다음을 수행하십시오.

    text_with_BOM = text if text.startswith(BOM) else BOM + text
    with open(filepath, mode='w', encoding='utf-16be') as f:
        f.write(text_with_BOM)
    

    이것은 모든 인코딩에서 작동합니다. UTF-16 빅 엔디안은 그 예입니다.

    이건 차 르벳을 기각하는 것이 아닙니다. 파일 인코딩에 사용 된 정보가없는 경우 도움이 될 수 있습니다. BOM을 추가 / 제거 할 필요가 없습니다.

  6. ==============================

    6.@ ivan_pozdeev의 문자열 / 예외 (파일이 아닌)에 대한 대답의 변형입니다. 파이썬 예외 (http://bugs.python.org/issue2517 참조)에서 채워진 유니 코드 HTML 컨텐트를 다루고 있습니다.

    @ ivan_pozdeev의 문자열 / 예외 (파일이 아닌)에 대한 대답의 변형입니다. 파이썬 예외 (http://bugs.python.org/issue2517 참조)에서 채워진 유니 코드 HTML 컨텐트를 다루고 있습니다.

    def detect_encoding(bytes_str):
      for enc, boms in \
          ('utf-8-sig',(codecs.BOM_UTF8,)),\
          ('utf-16',(codecs.BOM_UTF16_LE,codecs.BOM_UTF16_BE)),\
          ('utf-32',(codecs.BOM_UTF32_LE,codecs.BOM_UTF32_BE)):
        if (any(bytes_str.startswith(bom) for bom in boms): return enc
      return 'utf-8' # default
    
    def safe_exc_to_str(exc):
      try:
        return str(exc)
      except UnicodeEncodeError:
        return unicode(exc).encode(detect_encoding(exc.content))
    

    또는,이 훨씬 간단한 코드는 많은 비싸지 않고 비 ASCII 문자를 삭제할 수 있습니다.

    def just_ascii(str):
      return unicode(str).encode('ascii', 'ignore')
    
  7. from https://stackoverflow.com/questions/13590749/reading-unicode-file-data-with-bom-chars-in-python by cc-by-sa and MIT license