Metin kodlaması nasıl belirlenir?


Yanıtlar:


225

Kodlamanın her zaman doğru bir şekilde algılanması mümkün değildir .

(Chardet SSS'den :)

Ancak, bazı kodlamalar belirli diller için optimize edilmiştir ve diller rastgele değildir. Bazı karakter dizileri her zaman açılırken, diğer diziler anlamsızdır. Bir gazete açan ve “txzqJv 2! Dasd0a QqdKjvz” bulan akıcı bir kişi, İngilizce olmadığını anlar (tamamen İngilizce harflerden oluşmasına rağmen). Bir çok "tipik" metin üzerinde çalışarak, bir bilgisayar algoritması bu tür akıcılığı simüle edebilir ve bir metnin dili hakkında eğitimli bir tahminde bulunabilir.

Orada Chardet kullandığı çalışma kodlayan algılamak için denemek için o kütüphane. chardet, Mozilla'daki otomatik algılama kodunun bir bağlantı noktasıdır.

UnicodeDammit'i de kullanabilirsiniz . Aşağıdaki yöntemleri deneyecek:

  • Belgenin kendisinde bulunan bir kodlama: örneğin, bir XML bildiriminde veya (HTML belgeleri için) bir http-equiv META etiketi. Güzel Çorba bu tür kodlamayı belge içinde bulursa, belgeyi baştan tekrar ayrıştırır ve yeni kodlamayı dener. Tek istisna, açıkça bir kodlama belirlediyseniz ve bu kodlama gerçekten işe yaradıysa: belgede bulduğu herhangi bir kodlamayı yok sayar.
  • Bir kodlama, dosyanın ilk birkaç baytına bakarak kokladı. Bu aşamada bir kodlama algılanırsa, UTF- * kodlamalarından, EBCDIC veya ASCII'den biri olacaktır.
  • Yüklüyse , chardet kütüphanesi tarafından koklanan bir kodlama .
  • UTF-8
  • Windows-1252

1
chardetReferans için teşekkürler . Biraz yavaş olmasına rağmen iyi görünüyor.
Craig McQueen

17
@ Geomorillo: "Kodlama standardı" diye bir şey yoktur. Metin kodlaması bilgi işlem kadar eskidir, organik olarak zaman ve ihtiyaçlarla büyümüştür, planlanmamıştır. "Unicode" bunu düzeltmeye yönelik bir girişimdir.
nosklo

1
Ve kötü bir şey değil, her şey düşünüldü. Bilmek istediğim, açık bir metin dosyasının hangi kodlama ile açıldığını nasıl bulabilirim?
holdenweb

2
@dumbledad dediğim her zaman doğru bir şekilde tespit etmek imkansız. Tek yapabileceğiniz bir tahmindir, ancak bazen başarısız olabilir, kodlamalar gerçekten tespit edilemediği için her seferinde işe yaramaz. Tahmin etmek için, cevapta önerdiğim araçlardan birini kullanabilirsiniz
nosklo

1
@ LasseKärkkäinen bu cevabın amacı kodlamanın doğru bir şekilde tespit edilmesinin imkansız olduğunu göstermektir ; sağladığınız işlev sizin durumunuz için doğru tahmin edebilir, ancak birçok durumda yanlıştır.
nosklo

67

Kodlamayı çözmek için başka bir seçenek libmagic kullanmaktır ( dosya komutunun arkasındaki koddur ). Mevcut python bağları bolluğu vardır.

Dosya kaynak ağacında yaşayan python bağlamaları python-magic (veya python3-magic ) debian paketi olarak mevcuttur. Bir dosyanın kodlamasını yaparak şunları yapabilir:

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

Pypi'de aynı şekilde adlandırılan, ancak uyumsuz bir python-magic pip paketi var libmagic. Ayrıca, kodlamayı da yapabilir:

import magic

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

5
libmagicgerçekten de uygulanabilir bir alternatiftir chardet. Ve farklı paketler hakkında harika bilgiler python-magic! Eminim bu belirsizlik birçok insanı ısırır
MestreLion

1
filemetin dosyalarındaki insan dilini tanımlamada özellikle iyi değildir. Çeşitli kapsayıcı formatlarını tanımlamak için mükemmeldir, ancak bazen bunun ne anlama geldiğini bilmek zorundasınız ("Microsoft Office belgesi" bir Outlook mesajı, vb. Anlamına gelebilir).
Üçlü

Bu yazıyı buldum dosya kodlama gizem yönetmek için bir yol arıyorum. Ne yazık ki, örnek kodu kullanarak, ben geçemiyorum open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Kodlayan vim yıllardan göre dosya :set fileencodingolup latin1.
xtian

İsteğe bağlı bağımsız değişkeni kullanırsam, errors='ignore'örnek kodun çıktısı daha az yardımcı olur binary.
xtian

2
@xtian İkili modda açmanız gerekir, yani açık ("dosyaadı.txt", "rb").
L. Kärkkäinen

31

Bazı kodlama stratejileri, lütfen tadına bakın:

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

Dosyayı bir döngü biçiminde açıp okuyarak kodlamayı kontrol etmek isteyebilirsiniz ... ancak önce dosya boyutunu kontrol etmeniz gerekebilir:

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              

Ayrıca kullanabilirsiniz iogibi io.open(filepath, 'r', encoding='utf-8')çünkü daha uygun olan codecsdönüştürmez \nokuma ve yazma üzerine otomatik. HERE
Searene hakkında

23

Aşağıda, chardetkodlama tahminini okuma ve alma değerinin n_linesbüyük olması durumunda dosyadan okuma örneği verilmiştir .

chardetAyrıca, confidencekodlama tahmini (bunun nasıl ortaya çıktıklarına bakmadım) olasılığı da verir , bu da onun tahmini ile döndürülür chardet.predict(), böylece isterseniz bir şekilde çalışabilirsiniz.

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']

Yukarı oy aldıktan sonra buna bakarak ve şimdi ilk satırda çok fazla veri varsa bu çözümün yavaşlayabileceğini görün. Bazı durumlarda verileri farklı şekilde okumak daha iyi olur.
ryanjdillon

2
Bu işlevi şu şekilde değiştirdim: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) Python 3.6 üzerinde bu işlevi denedim, "ascii", "cp1252", "utf-8", "unicode" kodlamaları ile mükemmel çalıştı. Yani bu kesinlikle upvote.
n158

1
bu, çeşitli biçimlere sahip küçük veri kümelerini işlemek için çok iyidir. Bu kök dizin üzerinde özyinelemeli test ve bir tedavi gibi çalıştı. Sağol kanka.
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

Platformunuza bağlı olarak, linux shell filekomutunu kullanmayı tercih ediyorum . Özellikle benim linux makinelerimizden birinde çalışan bir senaryoda kullandığım için bu benim için çalışıyor.

Açıkçası bu ideal bir çözüm veya cevap değildir, ancak ihtiyaçlarınıza uyacak şekilde değiştirilebilir. Benim durumumda sadece bir dosyanın UTF-8 olup olmadığını belirlemem gerekiyor.

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')

Yeni bir işlemin çatallanması gerekmez. Python kodu zaten bir işlemin içinde çalışır ve yeni bir işlem yüklemesi olmadan uygun sistem işlevlerini kendisi çağırabilir.
vdboor

2

Bu yardımcı olabilir

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

Prensipte, bir metin dosyasının kodlamasını genel durumda belirlemek imkansızdır. Yani hayır, bunu sizin için yapacak standart bir Python kütüphanesi yok.

Metin dosyası hakkında daha spesifik bilgiye sahipseniz (ör. XML olduğu), kütüphane işlevleri olabilir.


1

Dosyanın bazı içeriğini biliyorsanız birkaç kodlamayla kodunu çözmeyi ve hangisinin eksik olduğunu görmeyi deneyebilirsiniz. Genel olarak, bir metin dosyası bir metin dosyası olduğundan ve bunlar aptal olduğu için bir yol yoktur;)


1

Bu site, ASCII'yi tanımak, bombalarla kodlamak ve utf8 bom yok: https://unicodebook.readthedocs.io/guess_encoding.html . Dosyayı bayt dizisine (veri) okuyun: http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . İşte bir örnek. Osx'tayım.

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

Bir çözümün bağlantısı hoş karşılanır, ancak lütfen cevabınızın onsuz faydalı olduğundan emin olun: bağlantınızın çevresine bağlam ekleyin, böylece diğer kullanıcılarınız ne olduğu ve neden orada olduğu hakkında bir fikir edinebilir, ardından sayfanın en alakalı bölümünü alıntılayabilirsiniz ' hedef sayfanın kullanılamaması durumunda bağlantı kuruluyor. Bir bağlantıdan biraz daha fazla olan cevaplar silinebilir.
çift ​​bip
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.