Wie bestimme ich die Kodierung von Text?

Antworten:

225

Es ist unmöglich , die Codierung jederzeit korrekt zu erkennen .

(Von chardet FAQ :)

Einige Codierungen sind jedoch für bestimmte Sprachen optimiert, und Sprachen sind nicht zufällig. Einige Zeichenfolgen werden ständig angezeigt, während andere Sequenzen keinen Sinn ergeben. Eine Person, die fließend Englisch spricht und eine Zeitung öffnet und „txzqJv 2! Dasd0a QqdKjvz“ findet, erkennt sofort, dass dies kein Englisch ist (obwohl es ausschließlich aus englischen Buchstaben besteht). Durch das Studieren vieler „typischer“ Texte kann ein Computeralgorithmus diese Art von Sprachkompetenz simulieren und eine fundierte Vermutung über die Sprache eines Textes anstellen.

Es gibt die Chardet- Bibliothek, die diese Studie verwendet, um zu versuchen, die Codierung zu erkennen. chardet ist ein Port des Codes für die automatische Erkennung in Mozilla.

Sie können auch UnicodeDammit verwenden . Es werden die folgenden Methoden ausprobiert:

  • Eine im Dokument selbst entdeckte Codierung: zum Beispiel in einer XML-Deklaration oder (für HTML-Dokumente) einem http-äquivalenten META-Tag. Wenn Beautiful Soup diese Art der Codierung im Dokument findet, wird das Dokument von Anfang an erneut analysiert und die neue Codierung ausprobiert. Die einzige Ausnahme ist, wenn Sie explizit eine Codierung angegeben haben und diese Codierung tatsächlich funktioniert hat: Dann werden alle im Dokument gefundenen Codierungen ignoriert.
  • Eine Codierung, die durch Betrachten der ersten Bytes der Datei erkannt wurde. Wenn zu diesem Zeitpunkt eine Codierung erkannt wird, handelt es sich um eine der UTF- * Codierungen, EBCDIC oder ASCII.
  • Eine Codierung, die von der Chardet- Bibliothek abgerufen wird , sofern Sie sie installiert haben.
  • UTF-8
  • Windows-1252
nosklo
quelle
1
Danke für den chardetHinweis. Scheint gut, wenn auch etwas langsam.
Craig McQueen
17
@ Geomorillo: Es gibt keinen "Kodierungsstandard". Die Textcodierung ist so alt wie das Rechnen, sie ist mit der Zeit und den Bedürfnissen organisch gewachsen und nicht geplant. "Unicode" ist ein Versuch, dies zu beheben.
Nosklo
1
Und keine schlechte, alles in allem. Was ich wissen möchte ist, wie finde ich heraus, mit welcher Codierung eine geöffnete Textdatei geöffnet wurde?
Holdenweb
2
@dumbledad was ich gesagt habe ist , dass es richtig Erfassung aller Zeiten unmöglich ist. Alles, was Sie tun können, ist eine Vermutung, aber es kann manchmal fehlschlagen, es wird nicht jedes Mal funktionieren, da Codierungen nicht wirklich erkennbar sind. Um die Vermutung zu machen, können Sie eines der Werkzeuge verwenden, die ich in der Antwort vorgeschlagen habe
nosklo
1
@ LasseKärkkäinen Mit dieser Antwort soll gezeigt werden, dass eine korrekte Erkennung der Codierung unmöglich ist . Die von Ihnen bereitgestellte Funktion kann für Ihren Fall richtig erraten, ist jedoch in vielen Fällen falsch.
Nosklo
67

Eine weitere Möglichkeit für die Arbeit der Codierung aus zu bedienen libmagic (das ist der Code hinter dem ist Datei - Befehl). Es gibt eine Fülle von Python-Bindungen.

Die Python-Bindungen, die im Dateiquellbaum gespeichert sind, sind als Debian- Paket für Python-Magic (oder Python3-Magic ) verfügbar . Es kann die Codierung einer Datei bestimmen, indem es:

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

Auf pypi gibt es ein identisch benanntes, aber nicht kompatibles Python-Magic- Pip-Paket, das ebenfalls verwendet wird libmagic. Es kann auch die Codierung erhalten, indem es Folgendes tut:

import magic

blob = open('unknown-file', 'rb').read()
m = magic.Magic(mime_encoding=True)
encoding = m.from_buffer(blob)
Hamish Downer
quelle
5
libmagicist in der Tat eine praktikable Alternative zu chardet. Und tolle Infos zu den verschiedenen genannten Paketen python-magic! Ich bin sicher, diese Mehrdeutigkeit beißt viele Menschen
MestreLion
1
fileist nicht besonders gut darin, die menschliche Sprache in Textdateien zu identifizieren. Es eignet sich hervorragend zum Identifizieren verschiedener Containerformate, obwohl Sie manchmal wissen müssen, was es bedeutet ("Microsoft Office-Dokument" kann eine Outlook-Nachricht usw. bedeuten).
Tripleee
Auf der Suche nach einer Möglichkeit, das Rätsel der Dateikodierung zu lösen, habe ich diesen Beitrag gefunden. Mit dem Beispielcode komme ich leider nicht vorbei open(): UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 169799: invalid start byte. Die Dateicodierung nach vim :set fileencodingist latin1.
Xtian
Wenn ich das optionale Argument verwende errors='ignore', ist die Ausgabe des Beispielcodes weniger hilfreich binary.
Xtian
2
@xtian Sie müssen im Binärmodus öffnen, dh öffnen ("filename.txt", "rb").
L. Kärkkäinen
31

Einige Kodierungsstrategien, bitte nach Geschmack auskommentieren:

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

Möglicherweise möchten Sie die Codierung überprüfen, indem Sie die Datei in Form einer Schleife öffnen und lesen. Möglicherweise müssen Sie jedoch zuerst die Dateigröße überprüfen:

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
quelle
Sie können auch iowie verwenden io.open(filepath, 'r', encoding='utf-8'), was bequemer ist, da beim Lesen und Schreiben codecsnicht \nautomatisch konvertiert wird . Mehr auf HIER
Searene
23

Hier ist ein Beispiel für das Lesen und Nehmen einer chardetCodierungsvorhersage zum Nennwert , wobei n_linesaus der Datei gelesen wird, falls sie groß ist.

chardetgibt Ihnen auch eine Wahrscheinlichkeit (dh confidence) für die Codierungsvorhersage (ich habe nicht nachgesehen, wie sie darauf gekommen sind), die mit der Vorhersage von zurückgegeben wird chardet.predict(), sodass Sie das irgendwie einarbeiten können, wenn Sie möchten.

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
quelle
Betrachten Sie dies nach einer Abstimmung und sehen Sie jetzt, dass sich diese Lösung verlangsamen könnte, wenn in der ersten Zeile viele Daten vorhanden wären. In einigen Fällen ist es besser, die Daten anders einzulesen.
Ryanjdillon
2
Ich habe diese Funktion folgendermaßen geändert: def predict_encoding(file_path, n=20): ... skip ... and then rawdata = b''.join([f.read() for _ in range(n)]) Diese Funktion wurde unter Python 3.6 ausprobiert und funktionierte perfekt mit den Codierungen "ascii", "cp1252", "utf-8" und "unicode". Das ist also definitiv positiv.
n158
1
Dies ist sehr gut für die Verarbeitung kleiner Datensätze mit einer Vielzahl von Formaten. Testete dies rekursiv auf meinem Root-Verzeichnis und es funktionierte wie ein Genuss. Danke Kumpel.
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
quelle
2

Abhängig von Ihrer Plattform entscheide ich mich einfach für den Linux-Shell- fileBefehl. Dies funktioniert bei mir, da ich es in einem Skript verwende, das ausschließlich auf einem unserer Linux-Computer ausgeführt wird.

Natürlich ist dies keine ideale Lösung oder Antwort, aber es könnte an Ihre Bedürfnisse angepasst werden. In meinem Fall muss ich nur feststellen, ob eine Datei UTF-8 ist oder nicht.

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
quelle
Ein neuer Prozess ist nicht erforderlich. Python-Code wird bereits in einem Prozess ausgeführt und kann die richtigen Systemfunktionen selbst aufrufen, ohne dass ein neuer Prozess geladen werden muss.
Vdboor
2

Dies könnte hilfreich sein

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
quelle
1

Im allgemeinen Fall ist es grundsätzlich unmöglich, die Kodierung einer Textdatei zu bestimmen. Also nein, es gibt keine Standard-Python-Bibliothek, die das für Sie erledigt.

Wenn Sie genauere Kenntnisse über die Textdatei haben (z. B. XML), gibt es möglicherweise Bibliotheksfunktionen.

Martin v. Löwis
quelle
1

Wenn Sie den Inhalt der Datei kennen, können Sie versuchen, sie mit mehreren Codierungen zu dekodieren und festzustellen, welche fehlen. Im Allgemeinen gibt es keine Möglichkeit, da eine Textdatei eine Textdatei ist und diese dumm sind;)

Martin Thurau
quelle
1

Diese Site enthält Python-Code zum Erkennen von ASCII, Codieren mit Boms und utf8 no bom: https://unicodebook.readthedocs.io/guess_encoding.html . Datei in Byte-Array (Daten) einlesen : http://www.codecodex.com/wiki/Read_a_file_into_a_byte_array . Hier ist ein Beispiel. Ich bin in 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
quelle
Ein Link zu einer Lösung ist willkommen, aber stellen Sie sicher, dass Ihre Antwort ohne sie nützlich ist: Fügen Sie dem Link einen Kontext hinzu, damit Ihre Mitbenutzer eine Vorstellung davon haben, was es ist und warum es dort ist, und zitieren Sie dann den relevantesten Teil der Seite, die Sie verwenden. erneutes Verknüpfen mit, falls die Zielseite nicht verfügbar ist. Antworten, die kaum mehr als ein Link sind, können gelöscht werden.
Doppel-Piepton