Wie kann ich die Codierung / Codepage einer Textdatei erkennen?

295

In unserer Anwendung erhalten wir Textdateien ( .txt, .csvusw.) aus verschiedenen Quellen. Beim Lesen enthalten diese Dateien manchmal Müll, da die Dateien in einer anderen / unbekannten Codepage erstellt wurden.

Gibt es eine Möglichkeit, die Codepage einer Textdatei (automatisch) zu erkennen?

Die detectEncodingFromByteOrderMarksauf dem StreamReaderKonstruktor, arbeiten für UTF8 und andere Unicode markiert Dateien, aber ich bin auf der Suche nach einer Möglichkeit , Code - Seiten zu erkennen, wie ibm850, windows1252.


Vielen Dank für Ihre Antworten, das habe ich getan.

Die Dateien, die wir erhalten, stammen von Endbenutzern und haben keine Ahnung von Codepages. Die Empfänger sind auch Endbenutzer. Mittlerweile wissen sie Folgendes über Codepages: Codepages existieren und sind ärgerlich.

Lösung:

  • Öffnen Sie die empfangene Datei im Editor und sehen Sie sich einen verstümmelten Text an. Wenn jemand François oder so heißt, können Sie dies mit Ihrer menschlichen Intelligenz erraten.
  • Ich habe eine kleine App erstellt, mit der der Benutzer die Datei öffnen und einen Text eingeben kann, von dem der Benutzer weiß, dass er in der Datei angezeigt wird, wenn die richtige Codepage verwendet wird.
  • Durchlaufen Sie alle Codepages und zeigen Sie diejenigen an, die eine Lösung mit dem vom Benutzer bereitgestellten Text bieten.
  • Wenn mehr als eine Codepage angezeigt wird, bitten Sie den Benutzer, mehr Text anzugeben.
GvS
quelle

Antworten:

260

Sie können die Codepage nicht erkennen, Sie müssen darüber informiert werden. Sie können die Bytes analysieren und erraten, aber das kann zu bizarren (manchmal amüsanten) Ergebnissen führen. Ich kann es jetzt nicht finden, aber ich bin sicher, dass Notepad dazu verleitet werden kann, englischen Text auf Chinesisch anzuzeigen.

Auf jeden Fall ist dies das, was Sie lesen müssen: Das absolute Minimum, das jeder Softwareentwickler unbedingt positiv über Unicode und Zeichensätze wissen muss (keine Ausreden!) .

Insbesondere sagt Joel:

Die wichtigste Tatsache über Codierungen

Wenn Sie alles, was ich gerade erklärt habe, vollständig vergessen haben, erinnern Sie sich bitte an eine äußerst wichtige Tatsache. Es ist nicht sinnvoll, eine Zeichenfolge zu haben, ohne zu wissen, welche Codierung sie verwendet. Sie können Ihren Kopf nicht mehr in den Sand stecken und so tun, als wäre "einfacher" Text ASCII. Es gibt keinen einfachen Text.

Wenn Sie eine Zeichenfolge, einen Speicher, eine Datei oder eine E-Mail-Nachricht haben, müssen Sie wissen, in welcher Codierung sie sich befindet, oder Sie können sie nicht interpretieren oder den Benutzern nicht korrekt anzeigen.

JV.
quelle
43
Ich habe diese Antwort aus zwei Gründen abgelehnt. Erstens ist es nicht hilfreich zu sagen, dass "Ihnen gesagt werden muss". Wer würde es mir sagen und über welches Medium würden sie es tun? Wenn ich derjenige bin, der die Datei gespeichert hat, wen würde ich fragen? Mich selber? Zweitens ist der Artikel als Ressource zur Beantwortung der Frage nicht besonders hilfreich. Der Artikel ist eher eine Geschichte der Codierung im David Sedaris-Stil. Ich schätze die Erzählung, aber sie beantwortet die Frage nicht einfach / direkt.
Genorama
9
@geneorama, ich denke, Joels Artikel geht besser auf Ihre Fragen ein als ich es jemals könnte, aber hier ist es ... Das Medium hängt sicherlich von der Umgebung ab, in der der Text empfangen wird. Besser, dass die Datei (oder was auch immer) diese Informationen enthält (ich denke an HTML und XML). Andernfalls sollte es der Person, die den Text sendet, gestattet sein, diese Informationen bereitzustellen. Wenn Sie derjenige waren, der die Datei erstellt hat, wie können Sie dann nicht wissen, welche Codierung sie verwendet?
JV.
4
@geneorama, Fortsetzung ... Schließlich denke ich, der Hauptgrund, warum der Artikel die Frage nicht einfach beantwortet, ist, dass es keine einfache Antwort auf diese Frage gibt. Wenn die Frage "Wie kann ich raten ..." wäre, hätte ich anders geantwortet.
JV.
1
@JV Ich habe später erfahren, dass xml / html die Zeichencodierung angeben kann, danke für die Erwähnung dieses nützlichen Leckerbissens.
Genorama
1
@JV "Datei erstellen" ist möglicherweise eine schlechte Wortwahl. Ich gehe davon aus, dass ein Benutzer die Codierung einer vom Benutzer generierten Datei angeben kann. Kürzlich habe ich mit Hive eine Datei aus einem Hadoop-Cluster "erstellt" und an einen FTP-Server übergeben, bevor ich sie auf verschiedene Client-Computer heruntergeladen habe. Das Ergebnis enthielt Unicode-Müll, aber ich weiß nicht, welcher Schritt das Problem verursacht hat. Ich habe die Kodierung nie explizit angegeben. Ich wünschte, ich könnte die Codierung bei jedem Schritt überprüfen.
Genorama
31

Wenn Sie Nicht-UTF-Codierungen (dh keine Stückliste) erkennen möchten, müssen Sie im Wesentlichen die Heuristik und die statistische Analyse des Textes durchführen. Vielleicht möchten Sie einen Blick auf das Mozilla-Papier zur universellen Zeichensatzerkennung werfen ( gleicher Link mit besserer Formatierung über Wayback Machine ).

Tomer Gabel
quelle
9
Lustigerweise erkennt meine Firefox 3.05-Installation diese Seite als UTF-8 und zeigt eine Reihe von Fragezeichen-in-Diamanten-Glyphen an, obwohl die Quelle ein Meta-Tag für Windows-1252 hat. Durch manuelles Ändern der Zeichenkodierung wird das Dokument korrekt angezeigt.
Devstuff
5
Ihr Satz "Wenn Sie Nicht-UTF-Codierungen (dh keine Stückliste) erkennen möchten" ist leicht irreführend. Der Unicode-Standard empfiehlt nicht, utf-8-Dokumenten eine Stückliste hinzuzufügen! (und diese Empfehlung oder deren Fehlen ist die Quelle vieler Kopfschmerzen). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Tao
Auf diese Weise können Sie UTF-8-Zeichenfolgen verketten, ohne redundante Stücklisten anzusammeln. Außerdem wird für UTF-8 im Gegensatz zu beispielsweise UTF-16 kein Byte-Order-Mark benötigt.
Sashoalm
26

Haben Sie C # -Port für Mozilla Universal Charset Detector ausprobiert ?

Beispiel von http://code.google.com/p/ude/

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}    
ITmeze
quelle
1
Funktionierte einwandfrei für Windows-1252.
Seebiscuit
Und wie können Sie damit eine Textdatei lesen, um damit eine Zeichenfolge zu erstellen? CharsetDetector gibt den Namen der Codierung im Zeichenfolgenformat zurück und das wars ...
Bartosz
@ Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle
15

Sie können die Codepage nicht erkennen

Das ist eindeutig falsch. Jeder Webbrowser verfügt über eine Art universellen Zeichensatzdetektor für Seiten, die keinerlei Hinweis auf eine Codierung enthalten. Firefox hat einen. Sie können den Code herunterladen und sehen, wie es funktioniert. Eine Dokumentation finden Sie hier . Grundsätzlich ist es eine Heuristik, die aber sehr gut funktioniert.

Bei einer angemessenen Textmenge ist es sogar möglich, die Sprache zu erkennen.

Hier ist eine andere, die ich gerade mit Google gefunden habe:

shoosh
quelle
39
"Heuristik" - der Browser erkennt sie also nicht ganz, sondern macht eine fundierte Vermutung. "funktioniert wirklich gut" - also funktioniert es dann nicht immer? Klingt für mich so, als wären wir uns einig.
JV.
10
Der Standard für HTML schreibt vor, dass der Zeichensatz, wenn er nicht durch das Dokument definiert ist, als UTF-8-codiert betrachtet werden sollte.
Jon Trauntvein
5
Was cool ist, wenn wir nicht nicht standardmäßige HTML-Dokumente lesen. Oder Nicht-HTML-Dokumente.
Kos
2
Diese Antwort ist falsch, also musste ich abstimmen. Zu sagen, es wäre falsch, dass Sie die Codepage nicht erkennen können, ist falsch. Sie können raten und Ihre Vermutungen können ziemlich gut sein, aber Sie können eine Codepage nicht "erkennen".
z80crew
1
@ JonTrauntvein Gemäß den HTML5-Spezifikationen führt a character encoding declaration is required even if the encoding is US-ASCII eine fehlende Deklaration dazu, dass ein heuristischer Algorithmus verwendet wird und nicht auf UTF8 zurückgegriffen wird.
z80crew
9

Ich weiß, dass es für diese Frage sehr spät ist und diese Lösung einige nicht ansprechen wird (aufgrund ihrer englischsprachigen Tendenz und des Fehlens statistischer / empirischer Tests), aber sie hat bei mir sehr gut funktioniert, insbesondere bei der Verarbeitung hochgeladener CSV-Daten:

http://www.architectshack.com/TextFileEncodingDetector.ashx

Vorteile:

  • Eingebaute Stücklistenerkennung
  • Standard- / Fallback-Codierung anpassbar
  • (meiner Erfahrung nach) ziemlich zuverlässig für westeuropäische Dateien, die einige exotische Daten (z. B. französische Namen) mit einer Mischung aus Dateien im UTF-8- und Latin-1-Stil enthalten - im Grunde genommen der Großteil der US- und westeuropäischen Umgebungen.

Hinweis: Ich bin derjenige, der diese Klasse geschrieben hat, also nimm sie offensichtlich mit einem Körnchen Salz! :) :)

Tao
quelle
7

Notepad ++ bietet diese Funktion sofort. Es unterstützt auch das Ändern.

Hegearon
quelle
7

Auf der Suche nach einer anderen Lösung habe ich das gefunden

https://code.google.com/p/ude/

Diese Lösung ist ziemlich schwer.

Ich brauchte eine grundlegende Codierungserkennung, basierend auf 4 ersten Bytes und wahrscheinlich einer XML-Zeichensatzerkennung. Deshalb habe ich einen Beispielquellcode aus dem Internet genommen und eine leicht modifizierte Version von hinzugefügt

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

geschrieben für Java.

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

Es reicht aus, wahrscheinlich die ersten 1024 Bytes aus der Datei zu lesen, aber ich lade die ganze Datei.

TarmoPikaro
quelle
7

Wenn jemand nach einer 93,9% igen Lösung sucht. Das funktioniert bei mir:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}
Magu
quelle
Sehr schöne Lösung. Man kann den Hauptteil von ReadAsString () leicht in eine Schleife zulässiger Codierungen einschließen, wenn mehr als 2 Codierungen (UTF-8 und ASCI 1252) zulässig sein sollen.
ViRuSTriNiTy
Nachdem ich unzählige Beispiele ausprobiert hatte, kam ich endlich zu Ihnen. Ich bin gerade an einem glücklichen Ort. lol Danke !!!!!!!
Sedrick
Dies ist möglicherweise nicht die Antwort auf die Erkennung von 1252 gegenüber 1250, aber es sollte unbedingt die Antwort auf "Erkennung von UTF-8" mit oder ohne Stückliste sein !!
Chuckc
4

Ich habe etwas Ähnliches in Python gemacht. Grundsätzlich benötigen Sie viele Beispieldaten aus verschiedenen Codierungen, die durch ein verschiebbares Zwei-Byte-Fenster aufgeschlüsselt und in einem Wörterbuch (Hash) gespeichert sind, das auf Byte-Paaren verschlüsselt ist und Werte für Codierungslisten liefert.

Mit diesem Wörterbuch (Hash) nehmen Sie Ihren Eingabetext und:

  • Wenn es mit einem Stücklistenzeichen beginnt ('\ xfe \ xff' für UTF-16-BE, '\ xff \ xfe' für UTF-16-LE, '\ xef \ xbb \ xbf' für UTF-8 usw.), I. Behandle es wie vorgeschlagen
  • Wenn nicht, nehmen Sie ein ausreichend großes Beispiel des Textes, nehmen Sie alle Bytepaare des Beispiels und wählen Sie die Codierung, die am seltensten aus dem Wörterbuch vorgeschlagen wird.

Wenn Sie auch UTF-codierte Texte abgetastet haben, die nicht mit einer Stückliste beginnen, werden im zweiten Schritt diejenigen behandelt, die aus dem ersten Schritt herausgerutscht sind.

Bisher funktioniert es bei mir (die Beispieldaten und nachfolgenden Eingabedaten sind Untertitel in verschiedenen Sprachen) mit abnehmenden Fehlerraten.

tzot
quelle
4

Das Tool "uchardet" macht dies gut, indem es Zeichenhäufigkeitsverteilungsmodelle für jeden Zeichensatz verwendet. Größere Dateien und "typischere" Dateien haben (offensichtlich) mehr Vertrauen.

Auf Ubuntu bist du einfach apt-get install uchardet.

Auf anderen Systemen finden Sie Quelle, Verwendung und Dokumente hier: https://github.com/BYVoid/uchardet

Erik Aronesty
quelle
Auf dem Mac über Homebrew:brew install uchardet
Paul B
3

Der Konstruktor der StreamReader-Klasse verwendet einen Parameter zum Erkennen der Codierung.

Leppie
quelle
Es ist nur "Codierung" Link hier .. und die Beschreibung sagt, wir müssen die Codierung bereitstellen ..
SurajS
@ SurajS: Schau dir die anderen Überladungen an.
Leppie
Der ursprüngliche Autor möchte die Codierung für eine Datei ermitteln, die möglicherweise nicht über den Stücklistenmarker verfügt. Der StreamReader erkennt die Codierung aus dem Stücklistenkopf gemäß Signatur. öffentlicher StreamReader (Stream Stream, Bool DetectEncodingFromByteOrderMarks)
Ibondre
1

Wenn Sie eine Verknüpfung zu einer C-Bibliothek herstellen können, können Sie diese verwenden libenca. Siehe http://cihar.com/software/enca/ . Von der Manpage:

Enca liest bestimmte Textdateien oder Standardeingaben, wenn keine angegeben sind, und verwendet Kenntnisse über deren Sprache (muss von Ihnen unterstützt werden) und eine Mischung aus Analyse, statistischer Analyse, Vermutung und schwarzer Magie, um ihre Codierungen zu bestimmen.

Es ist GPL v2.

Nick Matteo
quelle
0

Ich habe das gleiche Problem, aber noch keine gute Lösung gefunden, um es automatisch zu erkennen. Jetzt benutze ich PsPad (www.pspad.com) dafür;) Funktioniert gut

DeeCee
quelle
0

Da es sich im Wesentlichen um Heuristiken handelt, kann es hilfreich sein, die Codierung zuvor empfangener Dateien aus derselben Quelle als ersten Hinweis zu verwenden.

Die meisten Leute (oder Anwendungen) erledigen Dinge jedes Mal in der gleichen Reihenfolge, oft auf demselben Computer. Wenn Bob eine CSV-Datei erstellt und an Mary sendet, wird sie wahrscheinlich immer Windows-1252 oder verwenden was auch immer seine Maschine standardmäßig ist.

Wo möglich schadet auch ein bisschen Kundenschulung nie :-)

devstuff
quelle
0

Eigentlich suchte ich nach einer generischen, nicht programmierbaren Methode zum Erkennen der Dateicodierung, fand diese aber noch nicht. Beim Testen mit verschiedenen Codierungen stellte ich fest, dass mein Text UTF-7 war.

Also, wo ich zuerst war: StreamReader file = File.OpenText (vollständiger Dateiname);

Ich musste es ändern in: StreamReader file = new StreamReader (vollständiger Dateiname, System.Text.Encoding.UTF7);

OpenText geht davon aus, dass es sich um UTF-8 handelt.

Sie können den StreamReader auch wie diesen neuen StreamReader (vollständiger Dateiname, true) erstellen. Der zweite Parameter bedeutet, dass versucht werden soll, die Codierung anhand der Byteordermarkierung der Datei zu erkennen. In meinem Fall hat dies jedoch nicht funktioniert.

Intraday-Tipps
quelle
@ JohnMachin Ich stimme zu, dass es selten ist, aber es ist zB in einigen Teilen des IMAP-Protokolls vorgeschrieben. Wenn Sie dort sind, müssten Sie jedoch nicht raten.
Tripleee
0

Öffnen Sie die Datei in AkelPad (oder kopieren Sie einfach einen verstümmelten Text) und gehen Sie zu Bearbeiten -> Auswahl -> Neu codieren ... -> aktivieren Sie "Autodetect".

Plavozont
quelle
0

Als Addon zum ITmeze-Beitrag habe ich diese Funktion verwendet, um die Ausgabe des C # -Ports für Mozilla Universal Charset Detector zu konvertieren

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN

PrivatePyle
quelle
0

Vielen Dank an Erik Aronesty für die Erwähnung uchardet.

Mittlerweile gibt es das (gleiche?) Tool für Linux : chardet.
Oder Sie möchten auf cygwin Folgendes verwenden:chardetect .

Siehe: Chardet-Manpage: https://www.commandlinux.com/man-page/man1/chardetect.1.html

Dadurch wird die Zeichenkodierung für jede gegebene Datei heuristisch erkannt (erraten) und der Name und das Konfidenzniveau für die erkannte Zeichenkodierung jeder Datei angegeben.

Schlacki
quelle
-1

Ich verwende diesen Code, um beim Lesen einer Datei die Unicode- und Windows-Standard-Ansi-Codepage zu erkennen. Für andere Codierungen ist eine Überprüfung des Inhalts manuell oder durch Programmierung erforderlich. Dies kann verwendet werden, um den Text mit derselben Codierung wie beim Öffnen zu speichern. (Ich benutze VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()
Thommy Johansson
quelle
-1

10Y (!) War vergangen, seit dies gefragt wurde, und ich sehe immer noch keine Erwähnung der guten, nicht GPL-fähigen Lösung von MS: IMultiLanguage2 API.

Die meisten bereits erwähnten Bibliotheken basieren auf Mozillas UDE - und es scheint vernünftig, dass Browser bereits ähnliche Probleme gelöst haben. Ich weiß nicht, was die Lösung von Chrome ist, aber seit IE 5.0 haben MS ihre veröffentlicht, und es ist:

  1. Frei von GPL-ähnlichen Lizenzproblemen,
  2. Unterstützt und gepflegt wahrscheinlich für immer,
  3. Bietet reichhaltige Ausgabe - alle gültigen Kandidaten für Codierung / Codepages zusammen mit Konfidenzwerten,
  4. Überraschend einfach zu bedienen (es ist ein einzelner Funktionsaufruf).

Es ist ein nativer COM-Aufruf, aber hier ist eine sehr schöne Arbeit von Carsten Zeumer, die das Interop-Chaos für die .net-Nutzung behandelt. Es gibt einige andere, aber im Großen und Ganzen bekommt diese Bibliothek nicht die Aufmerksamkeit, die sie verdient.

Ofek Shilon
quelle