Bagaimana cara menentukan penyandian teks?

Jawaban:

225

Mendeteksi pengkodean dengan benar setiap saat adalah hal yang mustahil .

(Dari FAQ chardet :)

Namun, beberapa penyandian dioptimalkan untuk bahasa tertentu, dan bahasa tidak acak. Beberapa urutan karakter muncul setiap saat, sementara urutan lainnya tidak masuk akal. Seseorang yang fasih berbahasa Inggris yang membuka surat kabar dan menemukan "txzqJv 2! Dasd0a QqdKjvz" akan langsung mengenali bahwa itu bukan bahasa Inggris (meskipun seluruhnya terdiri dari huruf-huruf bahasa Inggris). Dengan mempelajari banyak teks "khas", algoritma komputer dapat mensimulasikan kefasihan jenis ini dan membuat tebakan yang berpendidikan tentang bahasa teks.

Ada perpustakaan chardet yang menggunakan studi itu untuk mencoba mendeteksi pengkodean. chardet adalah port dari kode deteksi otomatis di Mozilla.

Anda juga dapat menggunakan UnicodeDammit . Ini akan mencoba metode berikut:

  • Pengkodean ditemukan dalam dokumen itu sendiri: misalnya, dalam deklarasi XML atau (untuk dokumen HTML) tag META http-equiv. Jika Beautiful Soup menemukan penyandian semacam ini di dalam dokumen, itu mem-parsing dokumen itu lagi dari awal dan mencoba mencoba penyandian yang baru. Satu-satunya pengecualian adalah jika Anda secara eksplisit menentukan suatu pengkodean, dan pengkodean itu benar-benar berfungsi: maka ia akan mengabaikan segala pengodean yang ditemukan dalam dokumen.
  • Pengkodean mengendus dengan melihat beberapa byte pertama file. Jika suatu pengkodean terdeteksi pada tahap ini, itu akan menjadi salah satu pengkodean UTF- *, EBCDIC, atau ASCII.
  • Pengkodean didengus oleh pustaka chardet , jika Anda sudah menginstalnya.
  • UTF-8
  • Windows-1252
nosklo
sumber
1
Terima kasih untuk chardetreferensi. Tampak bagus, meski agak lambat.
Craig McQueen
17
@ Geomorillo: Tidak ada yang namanya "standar pengkodean". Pengkodean teks adalah sesuatu yang setua komputasi, ia tumbuh secara organik dengan waktu dan kebutuhan, itu tidak direncanakan. "Unicode" adalah upaya untuk memperbaikinya.
nosklo
1
Dan bukan yang buruk, semua hal dipertimbangkan. Yang ingin saya ketahui adalah, bagaimana cara mengetahui penyandian file teks terbuka apa yang dibuka?
holdenweb
2
@dumbledad apa yang saya katakan adalah bahwa mendeteksi dengan benar setiap saat adalah tidak mungkin. Yang dapat Anda lakukan hanyalah menebak, tetapi kadang-kadang bisa gagal, itu tidak akan berfungsi setiap saat, karena penyandian tidak benar-benar terdeteksi. Untuk melakukan tebakan, Anda dapat menggunakan salah satu alat yang saya sarankan dalam jawaban
nosklo
1
@ LasseKärkkäinen inti dari jawaban itu adalah untuk menunjukkan bahwa mendeteksi pengodean dengan benar adalah hal yang mustahil ; fungsi yang Anda berikan dapat menebak dengan tepat untuk kasus Anda, tetapi salah untuk banyak kasus.
nosklo
67

Pilihan lain untuk menyelesaikan encoding adalah menggunakan libmagic (yang merupakan kode di belakang perintah file ). Ada banyak sekali ikatan python yang tersedia.

Ikatan python yang hidup di pohon sumber file tersedia sebagai paket debian python-magic (atau python3-magic ). Itu dapat menentukan penyandian file dengan melakukan:

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

Ada paket pip python-magic yang bernama, tetapi tidak kompatibel pada pypi yang juga menggunakan libmagic. Itu juga bisa mendapatkan encoding, dengan melakukan:

import magic

blob = open('unknown-file', 'rb').read()
m = magic.Magic(mime_encoding=True)
encoding = m.from_buffer(blob)
Hamish Downer
sumber
5
libmagicmemang alternatif yang layak untuk chardet. Dan info hebat tentang paket berbeda bernama python-magic! Saya yakin ambiguitas ini menggigit banyak orang
MestreLion
1
filetidak terlalu pandai mengidentifikasi bahasa manusia dalam file teks. Ini sangat baik untuk mengidentifikasi berbagai format wadah, meskipun Anda kadang-kadang harus tahu apa artinya ("dokumen Microsoft Office" dapat berarti pesan Outlook, dll).
tripleee
Mencari cara untuk mengelola misteri penyandian file Saya menemukan posting ini. Sayangnya, menggunakan kode contoh, saya tidak bisa melewati open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Pengkodean file menurut vim :set fileencodingadalah latin1.
xtian
Jika saya menggunakan argumen opsional errors='ignore', output dari kode contoh kurang bermanfaat binary.
xtian
2
@xtian Anda harus membuka dalam mode biner, yaitu buka ("filename.txt", "rb").
L. Kärkkäinen
31

Beberapa strategi penyandian, harap batalkan komentar untuk mencicipi:

#!/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

Anda mungkin ingin memeriksa penyandian dengan membuka dan membaca file dalam bentuk loop ... tetapi Anda mungkin perlu memeriksa ukuran file terlebih dahulu:

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              
zzart
sumber
Anda juga dapat menggunakan io, seperti io.open(filepath, 'r', encoding='utf-8'), yang lebih nyaman, karena codecstidak mengkonversi \nsecara otomatis pada membaca dan menulis. Lebih lanjut tentang DI SINI
Searene
23

Berikut adalah contoh membaca dan menerima chardetprediksi pengkodean dengan nilai nominal , membaca n_linesdari file jika besar.

chardetjuga memberi Anda kemungkinan (yaitu confidence) dari prediksi pengkodean itu (belum melihat bagaimana mereka datang dengan itu), yang dikembalikan dengan prediksi dari chardet.predict(), sehingga Anda dapat bekerja dalam entah bagaimana jika Anda suka.

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
sumber
Melihat ini setelah mendapatkan suara dan sekarang melihat bahwa solusi ini bisa melambat jika ada banyak data di baris pertama. Dalam beberapa kasus akan lebih baik untuk membaca data secara berbeda.
ryanjdillon
2
Saya telah memodifikasi fungsi ini dengan cara ini: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) Telah mencoba fungsi ini pada Python 3.6, bekerja dengan sempurna dengan "ascii", "cp1252", "utf-8", "unicode" encodings. Jadi ini jelas upvote.
n158
1
ini sangat baik untuk menangani dataset kecil dengan berbagai format. Menguji ini secara rekursif pada dir root saya dan itu bekerja seperti memperlakukan. Terimakasih kawan.
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()
Bill Moore
sumber
2

Tergantung pada platform Anda, saya hanya memilih untuk menggunakan fileperintah shell linux . Ini berfungsi untuk saya karena saya menggunakannya dalam skrip yang secara eksklusif berjalan di salah satu mesin linux kami.

Jelas ini bukan solusi atau jawaban yang ideal, tetapi bisa dimodifikasi agar sesuai dengan kebutuhan Anda. Dalam kasus saya, saya hanya perlu menentukan apakah suatu file adalah UTF-8 atau tidak.

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')
MikeD
sumber
Tidak perlu forking proses baru. Kode python sudah berjalan di dalam suatu proses, dan dapat memanggil fungsi sistem yang tepat itu sendiri tanpa overhead memuat proses baru.
vdboor
2

Ini mungkin bermanfaat

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'
richinex
sumber
1

Pada prinsipnya, tidak mungkin untuk menentukan pengkodean file teks, dalam kasus umum. Jadi tidak, tidak ada perpustakaan Python standar untuk melakukan itu untuk Anda.

Jika Anda memiliki pengetahuan yang lebih spesifik tentang file teks (mis. Itu XML), mungkin ada fungsi perpustakaan.

Martin v. Löwis
sumber
1

Jika Anda mengetahui beberapa konten file, Anda dapat mencoba mendekodekannya dengan beberapa penyandian dan melihat mana yang hilang. Secara umum tidak ada cara karena file teks adalah file teks dan itu bodoh;)

Martin Thurau
sumber
1

Situs ini memiliki kode python untuk mengenali ascii, pengodean dengan boms, dan utf8 no bom: https://unicodebook.readthedocs.io/guess_encoding.html . Baca file ke dalam array byte (data): http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Ini sebuah contoh. Saya di 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
js2010
sumber
Tautan ke suatu solusi disambut baik, tetapi harap pastikan jawaban Anda bermanfaat tanpanya: tambahkan konteks di sekitar tautan sehingga teman-teman Anda akan mengetahui apa itu dan mengapa ada, lalu kutip bagian yang paling relevan dari halaman yang Anda tuju. menghubungkan kembali jika seandainya halaman target tidak tersedia. Jawaban yang sedikit lebih dari sebuah tautan dapat dihapus.
bip ganda