テキストのエンコードを決定する方法は?


回答:


225

エンコーディングを常に正しく検出することは不可能です。

(chardet FAQから:)

ただし、一部のエンコーディングは特定の言語用に最適化されており、言語はランダムではありません。一部の文字シーケンスは常にポップアップしますが、他のシーケンスは意味がありません。新聞を開いて「txzqJv 2!dasd0a QqdKjvz」を見つけた英語に堪能な人は、それが英語ではないことを即座に認識します(英語の文字だけで構成されていますが)。多くの「典型的な」テキストを研究することにより、コンピュータアルゴリズムはこの種の流暢さをシミュレートし、テキストの言語について知識のある推測を行うことができます。

その研究を使用してエンコーディングを検出しようとするchardetライブラリがあります。chardetは、Mozillaの自動検出コードの移植版です。

UnicodeDammitを使用することもできます。次の方法を試します。

  • ドキュメント自体で発見されたエンコーディング:たとえば、XML宣言または(HTMLドキュメントの場合)http-equiv METAタグ。Beautiful Soupがドキュメント内でこの種のエンコーディングを検出すると、ドキュメントを最初から再度解析し、新しいエンコーディングを試します。唯一の例外は、明示的にエンコードを指定し、そのエンコードが実際に機能した場合です。ドキュメントで検出されたエンコードはすべて無視されます。
  • ファイルの最初の数バイトを調べて、エンコードされたエンコーディング。この段階でエンコーディングが検出された場合、エンコーディングはUTF- *エンコーディング、EBCDIC、またはASCIIのいずれかになります。
  • chardetライブラリーによってスニッフィングされたエンコード(インストールされている場合)。
  • UTF-8
  • ウィンドウズ-1252

1
chardet参照いただきありがとうございます。少し遅いですが、良さそうです。
クレイグマックイーン

17
@Geomorillo:「エンコーディング標準」というものはありません。テキストエンコーディングはコンピューティングと同じくらい古いもので、時間とニーズに合わせて有機的に成長しましたが、計画されていませんでした。「Unicode」はこれを修正する試みです。
nosklo 2013

1
悪いことではなく、すべてを考慮しました。私が知りたいのは、オープンテキストファイルがどのエンコーディングで開かれたかを知る方法です。
holdenweb 2014年

2
@dumbledadが言ったことは、常に正しく検出することは不可能だということです。推測はできますが、失敗することもあります。エンコーディングが実際に検出されないため、毎回機能しません。推測を行うには、回答で提案したツールの1つを使用できます
nosklo

1
@LasseKärkkäinenその答えの要点は、正しくエンコードを検出することが不可能であることを示すことです。あなたが提供する関数はあなたの場合には正しいと推測できますが、多くの場合間違っています。
nosklo

67

エンコーディングを処理する別のオプションは、libmagicファイルコマンドの背後にある コード)を使用すること です。利用可能なPythonバインディングがたくさんあります。

ファイルソースツリーにあるpythonバインディングは、python-magic(またはpython3-magic)debianパッケージとして入手でき ます。それはファイルのエンコーディングを決定することができます:

import magic

blob = open('unknown-file', 'rb').read()
m = magic.open(magic.MAGIC_MIME_ENCODING)
m.load()
encoding = m.buffer(blob)  # "utf-8" "us-ascii" etc

同じ名前の、互換性のないpython-magic pipパッケージがpypiにあり、これもを使用していlibmagicます。次のようにして、エンコーディングを取得することもできます。

import magic

blob = open('unknown-file', 'rb').read()
m = magic.Magic(mime_encoding=True)
encoding = m.from_buffer(blob)

5
libmagicは確かにの実行可能な代替chardetです。という名前の異なるパッケージに関する素晴らしい情報python-magic!このあいまいさが多くの人に
噛み合っていると思います

1
fileテキストファイルで人間の言語を識別するのは特に得意ではありません。さまざまなコンテナー形式の識別には優れていますが、それが何を意味するのかを知っておく必要がある場合もあります( "Microsoft Officeドキュメント"はOutlookメッセージなどを意味する場合があります)。
tripleee

ファイルエンコーディングの謎を管理する方法を探して、この投稿を見つけました。残念ながら、サンプルコードを使用して、私は過去取得することはできませんopen()UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte。vimによるファイルエンコーディング:set fileencodinglatin1です。
xtian 2017

オプションの引数を使用するerrors='ignore'と、サンプルコードの出力はあまり役に立ちませんbinary
xtian 2017

2
@xtianバイナリモードで開く必要があります。つまり、open( "filename.txt"、 "rb")です。
L.Kärkkäinen19年

31

いくつかのエンコーディング戦略、コメントを外して味わってください:

#!/bin/bash
#
tmpfile=$1
echo '-- info about file file ........'
file -i $tmpfile
enca -g $tmpfile
echo 'recoding ........'
#iconv -f iso-8859-2 -t utf-8 back_test.xml > $tmpfile
#enca -x utf-8 $tmpfile
#enca -g $tmpfile
recode CP1250..UTF-8 $tmpfile

ループの形式でファイルを開いて読み取ることでエンコーディングを確認したい場合がありますが、最初にファイルサイズを確認する必要がある場合があります。

encodings = ['utf-8', 'windows-1250', 'windows-1252' ...etc]
            for e in encodings:
                try:
                    fh = codecs.open('file.txt', 'r', encoding=e)
                    fh.readlines()
                    fh.seek(0)
                except UnicodeDecodeError:
                    print('got unicode error with %s , trying different encoding' % e)
                else:
                    print('opening the file with encoding:  %s ' % e)
                    break              

io、のようio.open(filepath, 'r', encoding='utf-8')に使用することもできます。これは、が読み書き時に自動的にcodecs変換\nされないため、より便利です。詳細はこちら
Searene

23

これは、chardetエンコード予測を額面どおりに読み取って取得する例でありn_lines、ファイルが大きい場合はファイルから読み取ります。

chardetまたconfidence、からの予測とともに返されるエンコード予測の確率(つまり)を提供します(予測がどのように行われるかを確認していません)。そのchardet.predict()ため、必要に応じて何らかの方法でそれを実行できます。

def predict_encoding(file_path, n_lines=20):
    '''Predict a file's encoding using chardet'''
    import chardet

    # Open the file as binary data
    with open(file_path, 'rb') as f:
        # Join binary lines for specified number of lines
        rawdata = b''.join([f.readline() for _ in range(n_lines)])

    return chardet.detect(rawdata)['encoding']

賛成票を得た後でこれを見ると、最初の行に大量のデータがある場合、このソリューションが遅くなる可能性があることがわかります。場合によっては、別の方法でデータを読み取る方が良いでしょう。
ryanjdillon

2
この関数を次のように変更しました def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) 。Python3.6でこの関数を試し、「ascii」、「cp1252」、「utf-8」、「unicode」エンコーディングで完全に機能しました。これは間違いなく賛成です。
n158

1
これは、さまざまな形式の小さなデータセットを処理するのに非常に適しています。これを私のルートディレクトリで再帰的にテストしました。ありがとう。
Datanovice

4
# Function: OpenRead(file)

# A text file can be encoded using:
#   (1) The default operating system code page, Or
#   (2) utf8 with a BOM header
#
#  If a text file is encoded with utf8, and does not have a BOM header,
#  the user can manually add a BOM header to the text file
#  using a text editor such as notepad++, and rerun the python script,
#  otherwise the file is read as a codepage file with the 
#  invalid codepage characters removed

import sys
if int(sys.version[0]) != 3:
    print('Aborted: Python 3.x required')
    sys.exit(1)

def bomType(file):
    """
    returns file encoding string for open() function

    EXAMPLE:
        bom = bomtype(file)
        open(file, encoding=bom, errors='ignore')
    """

    f = open(file, 'rb')
    b = f.read(4)
    f.close()

    if (b[0:3] == b'\xef\xbb\xbf'):
        return "utf8"

    # Python automatically detects endianess if utf-16 bom is present
    # write endianess generally determined by endianess of CPU
    if ((b[0:2] == b'\xfe\xff') or (b[0:2] == b'\xff\xfe')):
        return "utf16"

    if ((b[0:5] == b'\xfe\xff\x00\x00') 
              or (b[0:5] == b'\x00\x00\xff\xfe')):
        return "utf32"

    # If BOM is not provided, then assume its the codepage
    #     used by your operating system
    return "cp1252"
    # For the United States its: cp1252


def OpenRead(file):
    bom = bomType(file)
    return open(file, 'r', encoding=bom, errors='ignore')


#######################
# Testing it
#######################
fout = open("myfile1.txt", "w", encoding="cp1252")
fout.write("* hi there (cp1252)")
fout.close()

fout = open("myfile2.txt", "w", encoding="utf8")
fout.write("\u2022 hi there (utf8)")
fout.close()

# this case is still treated like codepage cp1252
#   (User responsible for making sure that all utf8 files
#   have a BOM header)
fout = open("badboy.txt", "wb")
fout.write(b"hi there.  barf(\x81\x8D\x90\x9D)")
fout.close()

# Read Example file with Bom Detection
fin = OpenRead("myfile1.txt")
L = fin.readline()
print(L)
fin.close()

# Read Example file with Bom Detection
fin = OpenRead("myfile2.txt")
L =fin.readline() 
print(L) #requires QtConsole to view, Cmd.exe is cp1252
fin.close()

# Read CP1252 with a few undefined chars without barfing
fin = OpenRead("badboy.txt")
L =fin.readline() 
print(L)
fin.close()

# Check that bad characters are still in badboy codepage file
fin = open("badboy.txt", "rb")
fin.read(20)
fin.close()

2

プラットフォームによっては、Linuxシェルfileコマンドを使用することを選択します。これは、Linuxマシンの1つでのみ実行されるスクリプトで使用しているため、うまくいきます。

明らかにこれは理想的な解決策や回答ではありませんが、ニーズに合わせて変更することができます。私の場合、ファイルがUTF-8かどうかを判別する必要があるだけです。

import subprocess
file_cmd = ['file', 'test.txt']
p = subprocess.Popen(file_cmd, stdout=subprocess.PIPE)
cmd_output = p.stdout.readlines()
# x will begin with the file type output as is observed using 'file' command
x = cmd_output[0].split(": ")[1]
return x.startswith('UTF-8')

新しいプロセスをフォークする必要はありません。Pythonコードはすでにプロセス内で実行されており、新しいプロセスをロードするオーバーヘッドなしで適切なシステム関数自体を呼び出すことができます。
vdboor 2017

2

これは役に立ちます

from bs4 import UnicodeDammit
with open('automate_data/billboard.csv', 'rb') as file:
   content = file.read()

suggestion = UnicodeDammit(content)
suggestion.original_encoding
#'iso-8859-1'

1

原則として、テキストファイルのエンコーディングを決定することは、一般的には不可能です。ですから、そうするための標準的なPythonライブラリはありません。

テキストファイルについてより具体的な知識がある場合(XMLであるなど)、ライブラリ関数がある可能性があります。


1

ファイルのコンテンツがわかっている場合は、いくつかのエンコーディングでデコードして、どれが欠落しているかを確認できます。テキストファイルはテキストファイルであり、それらは愚かであるため、一般的に方法はありません;)


1

このサイトには、ASCIIを認識するためのPythonコード、bomsを使用したエンコード、およびutf8 no bomがあります:https ://unicodebook.readthedocs.io/guess_encoding.html 。ファイルをバイト配列(データ)に読み込みます:http : //www.codecodex.com/wiki/Read_a_file_into_a_byte_array。ここに例があります。私はosxにいます。

#!/usr/bin/python                                                                                                  

import sys

def isUTF8(data):
    try:
        decoded = data.decode('UTF-8')
    except UnicodeDecodeError:
        return False
    else:
        for ch in decoded:
            if 0xD800 <= ord(ch) <= 0xDFFF:
                return False
        return True

def get_bytes_from_file(filename):
    return open(filename, "rb").read()

filename = sys.argv[1]
data = get_bytes_from_file(filename)
result = isUTF8(data)
print(result)


PS /Users/js> ./isutf8.py hi.txt                                                                                     
True

ソリューションへのリンクを歓迎しますが、それがなくても回答が役立つことを確認してください。リンクの周りにコンテキストを追加して、他のユーザーがそれが何であるか、なぜそこにあるのかを理解し、ページの最も関連性の高い部分を引用してくださいターゲットページが利用できない場合にリンクし直します。リンクに過ぎない回答は削除される場合があります。
ダブルビープ音
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.