Welche Kodierungen? UTF-8 gegen UTF-16, Big gegen Little Endian? Oder beziehen Sie sich auf die alten MSDos-Codepages wie Shift-JIS oder Cyrillic usw.?
@Oded: Quote "Die Methode getEncoding () gibt die Codierung zurück, die für den Stream eingerichtet wurde (lesen Sie das JavaDoc). Die Codierung wird für Sie nicht erraten."
Fábio Antunes
2
Für einige Hintergrundinformationen ist joelonsoftware.com/articles/Unicode.html eine gute Lektüre. Wenn es eine Sache gibt, die Sie über Text wissen sollten, ist es, dass es keinen einfachen Text gibt.
Martijn
Antworten:
156
Die StreamReader.CurrentEncodingEigenschaft gibt selten die richtige Textdatei-Codierung für mich zurück. Ich habe größere Erfolge bei der Bestimmung der Endianness einer Datei erzielt, indem ich deren Byte Order Mark (BOM) analysiert habe. Wenn die Datei keine Stückliste hat, kann dies die Codierung der Datei nicht bestimmen.
* AKTUALISIERT am 08.04.2020, um die UTF-32LE-Erkennung einzuschließen und die korrekte Codierung für UTF-32BE zurückzugeben
/// <summary>/// Determines a text file's encoding by analyzing its byte order mark (BOM)./// Defaults to ASCII when detection of the text file's endianness fails./// </summary>/// <param name="filename">The text file to analyze.</param>/// <returns>The detected encoding.</returns>publicstaticEncodingGetEncoding(string filename){// Read the BOMvar bom =newbyte[4];
using (var file =newFileStream(filename,FileMode.Open,FileAccess.Read)){
file.Read(bom,0,4);}// Analyze the BOMif(bom[0]==0x2b&& bom[1]==0x2f&& bom[2]==0x76)returnEncoding.UTF7;if(bom[0]==0xef&& bom[1]==0xbb&& bom[2]==0xbf)returnEncoding.UTF8;if(bom[0]==0xff&& bom[1]==0xfe&& bom[2]==0&& bom[3]==0)returnEncoding.UTF32;//UTF-32LEif(bom[0]==0xff&& bom[1]==0xfe)returnEncoding.Unicode;//UTF-16LEif(bom[0]==0xfe&& bom[1]==0xff)returnEncoding.BigEndianUnicode;//UTF-16BEif(bom[0]==0&& bom[1]==0&& bom[2]==0xfe&& bom[3]==0xff)returnnew UTF32Encoding(true,true);//UTF-32BE// We actually have no idea what the encoding is if we reach this point, so// you may wish to return null instead of defaulting to ASCIIreturnEncoding.ASCII;}
+1. Dies funktionierte auch für mich (während dies bei detectEncodingFromByteOrderMarks nicht der Fall war). Ich habe "new FileStream (Dateiname, FileMode.Open, FileAccess.Read)" verwendet, um eine IOException zu vermeiden, da die Datei schreibgeschützt ist.
Polyfun
55
UTF-8-Dateien können ohne Stückliste sein. In diesem Fall wird ASCII falsch zurückgegeben.
user626528
3
Diese Antwort ist falsch. Mit Blick auf die Referenzquelle für StreamReader, ist , dass die Umsetzung , was mehr Menschen wollen. Sie erstellen neue Codierungen, anstatt die vorhandenen Encoding.UnicodeObjekte zu verwenden, sodass Gleichheitsprüfungen fehlschlagen (was ohnehin selten vorkommt, weil beispielsweise Encoding.UTF8unterschiedliche Objekte zurückgegeben werden können), aber (1) nicht das wirklich seltsame UTF-7-Format verwendet. (2) ist standardmäßig UTF-8, wenn keine Stückliste gefunden wird, und (3) kann überschrieben werden, um eine andere Standardcodierung zu verwenden.
Hangar
2
Ich hatte besseren Erfolg mit neuen StreamReader (Dateiname, wahr) .CurrentEncoding
Benoit
4
Der Code enthält einen grundlegenden Fehler. wenn Sie die Erkennung Big-Endian - UTF32 Signatur ( 00 00 FE FF), kehren Sie das System bereitgestellte Encoding.UTF32, das ist ein Little-Endian - Codierung (wie bereits erwähnt hier ). Und wie von @Nyerguds festgestellt, suchen Sie immer noch nicht nach UTF32LE mit Signatur FF FE 00 00(laut en.wikipedia.org/wiki/Byte_order_mark ). Wie dieser Benutzer feststellte, muss diese Prüfung vor den 2-Byte-Prüfungen erfolgen, da sie subsumiert.
Glenn Slayden
45
Der folgende Code funktioniert gut mit der StreamReaderKlasse:
using (var reader =newStreamReader(fileName, defaultEncodingIfNoBom,true)){
reader.Peek();// you need this!var encoding = reader.CurrentEncoding;}
Der Trick besteht darin, den PeekAufruf zu verwenden, andernfalls hat .NET nichts getan (und die Präambel, die Stückliste, nicht gelesen). Wenn Sie ReadXXXvor dem Überprüfen der Codierung einen anderen Aufruf verwenden, funktioniert dies natürlich auch.
Wenn die Datei keine Stückliste hat, wird die defaultEncodingIfNoBomCodierung verwendet. Es gibt auch einen StreamReader ohne diese Überladungsmethode (in diesem Fall wird die Standardcodierung (ANSI) als defaultEncodingIfNoBom verwendet). Ich empfehle jedoch, zu definieren, was Sie als Standardcodierung in Ihrem Kontext betrachten.
Ich habe dies erfolgreich mit Dateien mit Stückliste für UTF8, UTF16 / Unicode (LE & BE) und UTF32 (LE & BE) getestet. Es funktioniert nicht für UTF7.
Ich bekomme zurück, was als Standardcodierung eingestellt ist. Könnte mir etwas fehlen?
Ram
1
@ DRAM - dies kann passieren, wenn die Datei keine Stückliste hat
Simon Mourier
Danke @Simon Mourier. Ich erwarte nicht, dass mein PDF / jede Datei nicht geboren wird. Dieser Link stackoverflow.com/questions/4520184/… kann für jemanden hilfreich sein, der versucht, ohne Bom zu erkennen.
Ram
1
In Powershell musste ich $ reader.close () ausführen, sonst war das Schreiben gesperrt. foreach($filename in $args) { $reader = [System.IO.StreamReader]::new($filename, [System.Text.Encoding]::default,$true); $peek = $reader.Peek(); $reader.currentencoding | select bodyname,encodingname; $reader.close() }
js2010
@ SimonMourier Dies funktioniert nicht, wenn die Codierung der Datei istUTF-8 without BOM
Ozkan
12
Ich würde die folgenden Schritte versuchen:
1) Überprüfen Sie, ob ein Byte Order Mark vorhanden ist
2) Überprüfen Sie, ob die Datei UTF8 gültig ist
3) Verwenden Sie die lokale "ANSI" -Codepage (ANSI, wie Microsoft es definiert)
Schritt 2 funktioniert, weil die meisten Nicht-ASCII-Sequenzen in anderen Codepages als UTF8 kein gültiges UTF8 sind.
Dies scheint die korrektere Antwort zu sein, da die andere Antwort bei mir nicht funktioniert. Man kann es mit File.OpenRead und .Reading der ersten paar Bytes der Datei machen.
user420667
Schritt 2 ist jedoch eine ganze Reihe von Programmierarbeiten, um die Bitmuster zu überprüfen.
Nyerguds
1
Ich bin mir nicht sicher, ob die Dekodierung tatsächlich Ausnahmen auslöst oder ob sie nur die nicht erkannten Sequenzen durch '?' Ersetzt. Ich habe sowieso ein bisschen Musterprüfungsklasse geschrieben.
Nyerguds
3
Wenn Sie eine Instanz von erstellen Utf8Encoding, können Sie einen zusätzlichen Parameter übergeben, der bestimmt, ob eine Ausnahme ausgelöst werden soll oder ob Sie eine stille Datenbeschädigung bevorzugen.
CodesInChaos
1
Ich mag diese Antwort. Die meisten Codierungen (wie wahrscheinlich 99% Ihrer Anwendungsfälle) sind entweder UTF-8 oder ANSI (Windows-Codepage 1252). Sie können überprüfen, ob die Zeichenfolge das Ersatzzeichen (0xFFFD) enthält, um festzustellen, ob die Codierung fehlgeschlagen ist.
Ok, wenn Sie sich Sorgen um die Lizenz machen, können Sie diese verwenden. Als MIT lizenziert und können Sie es sowohl für Open Source- als auch für Closed Source-Software verwenden. nuget.org/packages/SimpleHelpers.FileEncoding
Alexei Agüero Alba
Die Lizenz ist MPL mit einer GPL-Option. The library is subject to the Mozilla Public License Version 1.1 (the "License"). Alternatively, it may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL").
10.
Es scheint, dass diese Gabel derzeit die aktivste ist und ein Nuget-Paket UDE.Netstandard hat. github.com/yinyue200/ude
jbtule
sehr nützliche Bibliothek, die mit vielen verschiedenen und ungewöhnlichen Codierungen fertig wird! Panzer!
Mshakurov
7
Bereitstellung der Implementierungsdetails für die von @CodesInChaos vorgeschlagenen Schritte:
1) Überprüfen Sie, ob ein Byte Order Mark vorhanden ist
2) Überprüfen Sie, ob die Datei UTF8 gültig ist
3) Verwenden Sie die lokale "ANSI" -Codepage (ANSI, wie Microsoft es definiert)
Schritt 2 funktioniert, weil die meisten Nicht-ASCII-Sequenzen in anderen Codepages als UTF8 kein gültiges UTF8 sind. https://stackoverflow.com/a/4522251/867248 erklärt die Taktik ausführlicher.
using System; using System.IO; using System.Text;// Using encoding from BOM or UTF8 if no BOM found,// check if the file is valid, by reading all lines// If decoding fails, use the local "ANSI" codepagepublicstringDetectFileEncoding(Stream fileStream){varUtf8EncodingVerifier=Encoding.GetEncoding("utf-8",newEncoderExceptionFallback(),newDecoderExceptionFallback());
using (var reader =newStreamReader(fileStream,Utf8EncodingVerifier,
detectEncodingFromByteOrderMarks:true, leaveOpen:true, bufferSize:1024)){string detectedEncoding;try{while(!reader.EndOfStream){var line = reader.ReadLine();}
detectedEncoding = reader.CurrentEncoding.BodyName;}catch(Exception e){// Failed to decode the file using the BOM/UT8. // Assume it's local ANSI
detectedEncoding ="ISO-8859-1";}// Rewind the stream
fileStream.Seek(0,SeekOrigin.Begin);return detectedEncoding;}}[Test]publicvoidTest1(){Stream fs =File.OpenRead(@".\TestData\TextFile_ansi.csv");var detectedEncoding =DetectFileEncoding(fs);
using (var reader =newStreamReader(fs,Encoding.GetEncoding(detectedEncoding))){// Consume your filevar line = reader.ReadLine();...
Danke dir! Das hat sich für mich gelöst. Aber ich würde es vorziehen, nur reader.Peek() anstelle von while (!reader.EndOfStream) { var line = reader.ReadLine(); }
Harison Silva
reader.Peek()liest nicht den ganzen Stream. Ich fand das bei größeren Streams Peek()unzureichend. Ich habe reader.ReadToEndAsync()stattdessen verwendet.
Gary Pendlebury
2
Die folgenden Codes sind meine Powershell-Codes, um zu bestimmen, ob einige cpp- oder h- oder ml-Dateien mit ISO-8859-1 (Latin-1) oder UTF-8 ohne Stückliste codiert sind, wenn beides nicht GB18030 ist. Ich bin ein Chinese, der in Frankreich arbeitet, und MSVC speichert als Latin-1 auf einem französischen Computer und als GB auf einem chinesischen Computer. Dies hilft mir, Codierungsprobleme beim Austausch von Quelldateien zwischen meinem System und meinen Kollegen zu vermeiden.
Der Weg ist einfach: Wenn alle Zeichen zwischen x00-x7E, ASCII, UTF-8 und Latin-1 liegen, sind alle gleich. Wenn ich jedoch eine Nicht-ASCII-Datei von UTF-8 lese, wird das Sonderzeichen angezeigt Versuchen Sie also, mit Latin-1 zu lesen. In Latin-1 ist zwischen \ x7F und \ xAF leer, während GB zwischen x00-xFF voll verwendet. Wenn ich also eine zwischen den beiden habe, ist es nicht Latin-1
Der Code ist in PowerShell geschrieben, verwendet jedoch .net, sodass er leicht in C # oder F # übersetzt werden kann.
.NET ist nicht sehr hilfreich, aber Sie können den folgenden Algorithmus ausprobieren:
Versuchen Sie, die Codierung nach Stückliste (Byte Order Mark) zu finden ... sehr wahrscheinlich nicht gefunden
Versuchen Sie, in verschiedene Codierungen zu analysieren
Hier ist der Anruf:
var encoding =FileHelper.GetEncoding(filePath);if(encoding ==null)thrownewException("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");
Hier ist der Code:
publicclassFileHelper{/// <summary>/// Determines a text file's encoding by analyzing its byte order mark (BOM) and if not found try parsing into diferent encodings /// Defaults to UTF8 when detection of the text file's endianness fails./// </summary>/// <param name="filename">The text file to analyze.</param>/// <returns>The detected encoding or null.</returns>publicstaticEncodingGetEncoding(string filename){var encodingByBOM =GetEncodingByBOM(filename);if(encodingByBOM !=null)return encodingByBOM;// BOM not found :(, so try to parse characters into several encodingsvar encodingByParsingUTF8 =GetEncodingByParsing(filename,Encoding.UTF8);if(encodingByParsingUTF8 !=null)return encodingByParsingUTF8;var encodingByParsingLatin1 =GetEncodingByParsing(filename,Encoding.GetEncoding("iso-8859-1"));if(encodingByParsingLatin1 !=null)return encodingByParsingLatin1;var encodingByParsingUTF7 =GetEncodingByParsing(filename,Encoding.UTF7);if(encodingByParsingUTF7 !=null)return encodingByParsingUTF7;returnnull;// no encoding found}/// <summary>/// Determines a text file's encoding by analyzing its byte order mark (BOM) /// </summary>/// <param name="filename">The text file to analyze.</param>/// <returns>The detected encoding.</returns>privatestaticEncodingGetEncodingByBOM(string filename){// Read the BOMvar byteOrderMark =newbyte[4];
using (var file =newFileStream(filename,FileMode.Open,FileAccess.Read)){
file.Read(byteOrderMark,0,4);}// Analyze the BOMif(byteOrderMark[0]==0x2b&& byteOrderMark[1]==0x2f&& byteOrderMark[2]==0x76)returnEncoding.UTF7;if(byteOrderMark[0]==0xef&& byteOrderMark[1]==0xbb&& byteOrderMark[2]==0xbf)returnEncoding.UTF8;if(byteOrderMark[0]==0xff&& byteOrderMark[1]==0xfe)returnEncoding.Unicode;//UTF-16LEif(byteOrderMark[0]==0xfe&& byteOrderMark[1]==0xff)returnEncoding.BigEndianUnicode;//UTF-16BEif(byteOrderMark[0]==0&& byteOrderMark[1]==0&& byteOrderMark[2]==0xfe&& byteOrderMark[3]==0xff)returnEncoding.UTF32;returnnull;// no BOM found}privatestaticEncodingGetEncodingByParsing(string filename,Encoding encoding){var encodingVerifier =Encoding.GetEncoding(encoding.BodyName,newEncoderExceptionFallback(),newDecoderExceptionFallback());try{
using (var textReader =newStreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks:true)){while(!textReader.EndOfStream){
textReader.ReadLine();// in order to increment the stream position}// all text parsed okreturn textReader.CurrentEncoding;}}catch(Exception ex){}returnnull;// }}
string path =@"path\to\your\file.ext";
using (StreamReader sr =newStreamReader(path,true)){while(sr.Peek()>=0){Console.Write((char)sr.Read());}//Test for the encoding after reading, or at least//after the first read.Console.WriteLine("The encoding used was {0}.", sr.CurrentEncoding);Console.ReadLine();Console.WriteLine();}
Antworten:
Die
StreamReader.CurrentEncoding
Eigenschaft gibt selten die richtige Textdatei-Codierung für mich zurück. Ich habe größere Erfolge bei der Bestimmung der Endianness einer Datei erzielt, indem ich deren Byte Order Mark (BOM) analysiert habe. Wenn die Datei keine Stückliste hat, kann dies die Codierung der Datei nicht bestimmen.* AKTUALISIERT am 08.04.2020, um die UTF-32LE-Erkennung einzuschließen und die korrekte Codierung für UTF-32BE zurückzugeben
quelle
StreamReader
, ist , dass die Umsetzung , was mehr Menschen wollen. Sie erstellen neue Codierungen, anstatt die vorhandenenEncoding.Unicode
Objekte zu verwenden, sodass Gleichheitsprüfungen fehlschlagen (was ohnehin selten vorkommt, weil beispielsweiseEncoding.UTF8
unterschiedliche Objekte zurückgegeben werden können), aber (1) nicht das wirklich seltsame UTF-7-Format verwendet. (2) ist standardmäßig UTF-8, wenn keine Stückliste gefunden wird, und (3) kann überschrieben werden, um eine andere Standardcodierung zu verwenden.00 00 FE FF
), kehren Sie das System bereitgestellteEncoding.UTF32
, das ist ein Little-Endian - Codierung (wie bereits erwähnt hier ). Und wie von @Nyerguds festgestellt, suchen Sie immer noch nicht nach UTF32LE mit SignaturFF FE 00 00
(laut en.wikipedia.org/wiki/Byte_order_mark ). Wie dieser Benutzer feststellte, muss diese Prüfung vor den 2-Byte-Prüfungen erfolgen, da sie subsumiert.Der folgende Code funktioniert gut mit der
StreamReader
Klasse:Der Trick besteht darin, den
Peek
Aufruf zu verwenden, andernfalls hat .NET nichts getan (und die Präambel, die Stückliste, nicht gelesen). Wenn SieReadXXX
vor dem Überprüfen der Codierung einen anderen Aufruf verwenden, funktioniert dies natürlich auch.Wenn die Datei keine Stückliste hat, wird die
defaultEncodingIfNoBom
Codierung verwendet. Es gibt auch einen StreamReader ohne diese Überladungsmethode (in diesem Fall wird die Standardcodierung (ANSI) als defaultEncodingIfNoBom verwendet). Ich empfehle jedoch, zu definieren, was Sie als Standardcodierung in Ihrem Kontext betrachten.Ich habe dies erfolgreich mit Dateien mit Stückliste für UTF8, UTF16 / Unicode (LE & BE) und UTF32 (LE & BE) getestet. Es funktioniert nicht für UTF7.
quelle
foreach($filename in $args) { $reader = [System.IO.StreamReader]::new($filename, [System.Text.Encoding]::default,$true); $peek = $reader.Peek(); $reader.currentencoding | select bodyname,encodingname; $reader.close() }
UTF-8 without BOM
Ich würde die folgenden Schritte versuchen:
1) Überprüfen Sie, ob ein Byte Order Mark vorhanden ist
2) Überprüfen Sie, ob die Datei UTF8 gültig ist
3) Verwenden Sie die lokale "ANSI" -Codepage (ANSI, wie Microsoft es definiert)
Schritt 2 funktioniert, weil die meisten Nicht-ASCII-Sequenzen in anderen Codepages als UTF8 kein gültiges UTF8 sind.
quelle
Utf8Encoding
, können Sie einen zusätzlichen Parameter übergeben, der bestimmt, ob eine Ausnahme ausgelöst werden soll oder ob Sie eine stille Datenbeschädigung bevorzugen.Überprüfen Sie dies.
UDE
Dies ist ein Port von Mozilla Universal Charset Detector und Sie können ihn so verwenden ...
quelle
The library is subject to the Mozilla Public License Version 1.1 (the "License"). Alternatively, it may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL").
Bereitstellung der Implementierungsdetails für die von @CodesInChaos vorgeschlagenen Schritte:
1) Überprüfen Sie, ob ein Byte Order Mark vorhanden ist
2) Überprüfen Sie, ob die Datei UTF8 gültig ist
3) Verwenden Sie die lokale "ANSI" -Codepage (ANSI, wie Microsoft es definiert)
Schritt 2 funktioniert, weil die meisten Nicht-ASCII-Sequenzen in anderen Codepages als UTF8 kein gültiges UTF8 sind. https://stackoverflow.com/a/4522251/867248 erklärt die Taktik ausführlicher.
quelle
reader.Peek()
anstelle vonwhile (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
liest nicht den ganzen Stream. Ich fand das bei größeren StreamsPeek()
unzureichend. Ich habereader.ReadToEndAsync()
stattdessen verwendet.Die folgenden Codes sind meine Powershell-Codes, um zu bestimmen, ob einige cpp- oder h- oder ml-Dateien mit ISO-8859-1 (Latin-1) oder UTF-8 ohne Stückliste codiert sind, wenn beides nicht GB18030 ist. Ich bin ein Chinese, der in Frankreich arbeitet, und MSVC speichert als Latin-1 auf einem französischen Computer und als GB auf einem chinesischen Computer. Dies hilft mir, Codierungsprobleme beim Austausch von Quelldateien zwischen meinem System und meinen Kollegen zu vermeiden.
Der Weg ist einfach: Wenn alle Zeichen zwischen x00-x7E, ASCII, UTF-8 und Latin-1 liegen, sind alle gleich. Wenn ich jedoch eine Nicht-ASCII-Datei von UTF-8 lese, wird das Sonderzeichen angezeigt Versuchen Sie also, mit Latin-1 zu lesen. In Latin-1 ist zwischen \ x7F und \ xAF leer, während GB zwischen x00-xFF voll verwendet. Wenn ich also eine zwischen den beiden habe, ist es nicht Latin-1
Der Code ist in PowerShell geschrieben, verwendet jedoch .net, sodass er leicht in C # oder F # übersetzt werden kann.
quelle
.NET ist nicht sehr hilfreich, aber Sie können den folgenden Algorithmus ausprobieren:
Hier ist der Anruf:
Hier ist der Code:
quelle
Suchen Sie hier nach c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
quelle
Es kann nützlich sein
quelle