@Oded: Quote "Metoda getEncoding () zwróci kodowanie, które zostało ustawione (przeczytaj JavaDoc) dla strumienia. Nie odgadnie kodowania.".
Fábio Antunes
2
Aby uzyskać dodatkowe informacje, dobrze przeczytać joelonsoftware.com/articles/Unicode.html . Jeśli jest jedna rzecz, którą powinieneś wiedzieć o tekście, to to, że nie ma czegoś takiego jak zwykły tekst.
Martijn
Odpowiedzi:
155
StreamReader.CurrentEncodingNieruchomość rzadko zwraca poprawny plik tekstowy kodujący dla mnie. Odniosłem większy sukces w określaniu endianness pliku, analizując jego znacznik kolejności bajtów (BOM). Jeśli plik nie ma BOM, nie może to określić kodowania pliku.
* ZAKTUALIZOWANO 4/08/2020 w celu uwzględnienia wykrywania UTF-32LE i przywrócenia prawidłowego kodowania dla UTF-32BE
/// <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. To też zadziałało dla mnie (podczas gdy DetectEncodingFromByteOrderMarks nie). Użyłem „new FileStream (nazwa pliku, FileMode.Open, FileAccess.Read)”, aby uniknąć IOException, ponieważ plik jest tylko do odczytu.
Polyfun
56
Pliki UTF-8 mogą być bez BOM, w tym przypadku zwróci nieprawidłowy ASCII.
user626528
3
Ta odpowiedź jest błędna. Patrząc na źródło odniesienia StreamReader, ta implementacja jest tym, czego więcej ludzi będzie chciało. Tworzą nowe kodowania zamiast używać istniejących Encoding.Unicodeobiektów, więc sprawdzanie równości zakończy się niepowodzeniem (co i tak może się rzadko zdarzać, ponieważ na przykład Encoding.UTF8może zwracać różne obiekty), ale (1) nie używa naprawdę dziwnego formatu UTF-7, (2) domyślnie UTF-8, jeśli nie zostanie znalezione BOM, a (3) można zastąpić, aby użyć innego domyślnego kodowania.
hangar
2
Miałem większy sukces z nowym StreamReaderem (nazwa pliku, prawda) .CurrentEncoding
Benoit
4
W kodzie występuje podstawowy błąd; po wykryciu big-endian UTF32 signature ( 00 00 FE FF), zwracasz dane dostarczone przez system Encoding.UTF32, które jest kodowaniem little-endian (jak wspomniano tutaj ). A także, jak zauważył @Nyerguds, nadal nie szukasz UTF32LE, który ma podpis FF FE 00 00(zgodnie z en.wikipedia.org/wiki/Byte_order_mark ). Jak zauważył ten użytkownik, ponieważ następuje podsumowanie, to sprawdzenie musi nastąpić przed sprawdzeniami 2-bajtowymi.
Glenn Slayden,
44
Poniższy kod działa dobrze dla mnie, używając StreamReaderklasy:
using (var reader =newStreamReader(fileName, defaultEncodingIfNoBom,true)){
reader.Peek();// you need this!var encoding = reader.CurrentEncoding;}
Sztuczka polega na użyciu Peekwywołania, w przeciwnym razie .NET nic nie zrobił (i nie przeczytał preambuły, BOM). Oczywiście, jeśli użyjesz innego ReadXXXwywołania przed sprawdzeniem kodowania, to też zadziała.
Jeśli plik nie ma BOM, defaultEncodingIfNoBomzostanie użyte kodowanie. Istnieje również StreamReader bez tej metody przeciążania (w tym przypadku domyślne kodowanie (ANSI) będzie używane jako defaultEncodingIfNoBom), ale zalecam zdefiniowanie tego, co uważasz za domyślne kodowanie w twoim kontekście.
Przetestowałem to pomyślnie z plikami z BOM dla UTF8, UTF16 / Unicode (LE i BE) i UTF32 (LE i BE). Nie działa z UTF7.
Wracam to, co zostało ustawione jako domyślne kodowanie. Czy mogłem czegoś przegapić?
Ram
1
@DRAM - może się to zdarzyć, jeśli plik nie ma BOM
Simon Mourier
Dzięki @Simon Mourier. Nie spodziewam się, że mój plik PDF / żaden plik nie będzie miał BOM. Ten link stackoverflow.com/questions/4520184/ ... może być pomocny dla kogoś, kto próbuje wykryć bez bom.
Ram
1
W PowerShell musiałem uruchomić $ reader.close (), bo inaczej było to zablokowane. 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
1
@SimonMourier To nie działa, jeśli kodowanie pliku toUTF-8 without BOM
Ozkan
11
Spróbowałbym następujących kroków:
1) Sprawdź, czy istnieje znacznik kolejności bajtów
2) Sprawdź, czy plik jest prawidłowy w formacie UTF8
3) Użyj lokalnej strony kodowej „ANSI” (ANSI zgodnie z definicją firmy Microsoft)
Krok 2 działa, ponieważ większość sekwencji innych niż ASCII w stronach kodowych innych niż UTF8 nie jest prawidłowym kodem UTF8.
Wydaje się, że jest to bardziej poprawna odpowiedź, ponieważ druga odpowiedź nie działa dla mnie. Można to zrobić za pomocą File.OpenRead i .Reading pierwszych kilku bajtów pliku.
user420667
1
Krok 2 to jednak cała masa prac programistycznych mających na celu sprawdzenie wzorców bitowych.
Nyerguds
1
Nie jestem pewien, czy dekodowanie faktycznie generuje wyjątki, czy po prostu zastępuje nierozpoznane sekwencje znakiem „?”. I tak poszedłem z napisaniem klasy sprawdzania wzorców bitowych.
Nyerguds
3
Podczas tworzenia instancji Utf8Encodingyou można przekazać dodatkowy parametr, który określa, czy należy zgłosić wyjątek, czy też wolisz ciche uszkodzenie danych.
CodesInChaos
1
Podoba mi się ta odpowiedź. Większość kodowań (jak prawdopodobnie 99% przypadków użycia) to UTF-8 lub ANSI (strona kodowa Windows 1252). Możesz sprawdzić, czy ciąg zawiera znak zastępczy (0xFFFD), aby określić, czy kodowanie nie powiodło się.
Ok, jeśli martwisz się o licencję, możesz użyć tej. Licencjonowany jako MIT i można go używać zarówno do oprogramowania open source, jak i oprogramowania zamkniętego. nuget.org/packages/SimpleHelpers.FileEncoding
Alexei Agüero Alba
Licencja to MPL z opcją GPL. 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").
jbtule
Wygląda na to, że ten fork jest obecnie najbardziej aktywny i zawiera pakiet NuGet UDE.Netstandard. github.com/yinyue200/ude
jbtule
bardzo przydatna biblioteka, poradziła sobie z wieloma różnymi i nietypowymi kodowaniami! czołgi!
mshakurov
6
Podanie szczegółów implementacji dla kroków proponowanych przez @CodesInChaos:
1) Sprawdź, czy istnieje znacznik kolejności bajtów
2) Sprawdź, czy plik jest prawidłowy w formacie UTF8
3) Użyj lokalnej strony kodowej „ANSI” (ANSI zgodnie z definicją firmy Microsoft)
Krok 2 działa, ponieważ większość sekwencji innych niż ASCII w stronach kodowych innych niż UTF8 nie jest prawidłowym kodem UTF8. https://stackoverflow.com/a/4522251/867248 wyjaśnia szczegółowo taktykę.
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();...
Dziękuję Ci! To rozwiązało dla mnie. Ale wolałbym używać tylko reader.Peek() zamiast while (!reader.EndOfStream) { var line = reader.ReadLine(); }
Harison Silva,
reader.Peek()nie czyta całego strumienia. Okazało się, że przy większych strumieniach Peek()było nieodpowiednie. reader.ReadToEndAsync()Zamiast tego użyłem .
Gary Pendlebury
A co to jest Utf8EncodingVerifier?
Peter Moore
1
@PeterMoore Jest to kodowanie dla utf8, var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());jest używane w trybloku podczas odczytu linii. Jeśli koder nie może przeanalizować podanego tekstu (tekst nie jest zakodowany za pomocą utf8), zostanie zgłoszony przez Utf8EncodingVerifier. Wyjątek został przechwycony, a następnie wiemy, że tekst nie jest utf8 i domyślnie ISO-8859-1
Berthier Lemieux
2
Poniższe kody są moimi kodami Powershell do określenia, czy niektóre pliki cpp, h lub ml są kodowane za pomocą ISO-8859-1 (Latin-1) lub UTF-8 bez BOM, jeśli żaden z nich nie powinien być GB18030. Jestem Chińczykiem pracującym we Francji i MSVC zapisuje jako Latin-1 na francuskim komputerze i zapisuje jako GB na chińskim komputerze, więc pomaga mi to uniknąć problemów z kodowaniem podczas wymiany plików źródłowych między moim systemem a kolegami.
Sposób jest prosty, jeśli wszystkie znaki są między x00-x7E, ASCII, UTF-8 i Latin-1 są takie same, ale jeśli przeczytam plik inny niż ASCII przez UTF-8, znajdziemy znak specjalny pojawi się , więc spróbuj czytać z Latin-1. W Latin-1 między \ x7F a \ xAF jest puste, podczas gdy GB używa pełnego między x00-xFF, więc jeśli mam coś między nimi, nie jest to Latin-1
Kod jest napisany w PowerShell, ale używa .net, więc można go łatwo przetłumaczyć na C # lub F #
.NET nie jest zbyt pomocny, ale możesz wypróbować następujący algorytm:
spróbuj znaleźć kodowanie według BOM (znak kolejności bajtów) ... bardzo prawdopodobne, że nie zostanie znaleziony
spróbuj przeanalizować różne kodowania
Oto wezwanie:
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");
Oto kod:
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();}
Odpowiedzi:
StreamReader.CurrentEncoding
Nieruchomość rzadko zwraca poprawny plik tekstowy kodujący dla mnie. Odniosłem większy sukces w określaniu endianness pliku, analizując jego znacznik kolejności bajtów (BOM). Jeśli plik nie ma BOM, nie może to określić kodowania pliku.* ZAKTUALIZOWANO 4/08/2020 w celu uwzględnienia wykrywania UTF-32LE i przywrócenia prawidłowego kodowania dla UTF-32BE
źródło
StreamReader
, ta implementacja jest tym, czego więcej ludzi będzie chciało. Tworzą nowe kodowania zamiast używać istniejącychEncoding.Unicode
obiektów, więc sprawdzanie równości zakończy się niepowodzeniem (co i tak może się rzadko zdarzać, ponieważ na przykładEncoding.UTF8
może zwracać różne obiekty), ale (1) nie używa naprawdę dziwnego formatu UTF-7, (2) domyślnie UTF-8, jeśli nie zostanie znalezione BOM, a (3) można zastąpić, aby użyć innego domyślnego kodowania.00 00 FE FF
), zwracasz dane dostarczone przez systemEncoding.UTF32
, które jest kodowaniem little-endian (jak wspomniano tutaj ). A także, jak zauważył @Nyerguds, nadal nie szukasz UTF32LE, który ma podpisFF FE 00 00
(zgodnie z en.wikipedia.org/wiki/Byte_order_mark ). Jak zauważył ten użytkownik, ponieważ następuje podsumowanie, to sprawdzenie musi nastąpić przed sprawdzeniami 2-bajtowymi.Poniższy kod działa dobrze dla mnie, używając
StreamReader
klasy:Sztuczka polega na użyciu
Peek
wywołania, w przeciwnym razie .NET nic nie zrobił (i nie przeczytał preambuły, BOM). Oczywiście, jeśli użyjesz innegoReadXXX
wywołania przed sprawdzeniem kodowania, to też zadziała.Jeśli plik nie ma BOM,
defaultEncodingIfNoBom
zostanie użyte kodowanie. Istnieje również StreamReader bez tej metody przeciążania (w tym przypadku domyślne kodowanie (ANSI) będzie używane jako defaultEncodingIfNoBom), ale zalecam zdefiniowanie tego, co uważasz za domyślne kodowanie w twoim kontekście.Przetestowałem to pomyślnie z plikami z BOM dla UTF8, UTF16 / Unicode (LE i BE) i UTF32 (LE i BE). Nie działa z UTF7.
źródło
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
Spróbowałbym następujących kroków:
1) Sprawdź, czy istnieje znacznik kolejności bajtów
2) Sprawdź, czy plik jest prawidłowy w formacie UTF8
3) Użyj lokalnej strony kodowej „ANSI” (ANSI zgodnie z definicją firmy Microsoft)
Krok 2 działa, ponieważ większość sekwencji innych niż ASCII w stronach kodowych innych niż UTF8 nie jest prawidłowym kodem UTF8.
źródło
Utf8Encoding
you można przekazać dodatkowy parametr, który określa, czy należy zgłosić wyjątek, czy też wolisz ciche uszkodzenie danych.Sprawdź to.
UDE
To jest port Mozilla Universal Charset Detector i możesz go używać w ten sposób ...
źródło
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").
Podanie szczegółów implementacji dla kroków proponowanych przez @CodesInChaos:
1) Sprawdź, czy istnieje znacznik kolejności bajtów
2) Sprawdź, czy plik jest prawidłowy w formacie UTF8
3) Użyj lokalnej strony kodowej „ANSI” (ANSI zgodnie z definicją firmy Microsoft)
Krok 2 działa, ponieważ większość sekwencji innych niż ASCII w stronach kodowych innych niż UTF8 nie jest prawidłowym kodem UTF8. https://stackoverflow.com/a/4522251/867248 wyjaśnia szczegółowo taktykę.
źródło
reader.Peek()
zamiastwhile (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
nie czyta całego strumienia. Okazało się, że przy większych strumieniachPeek()
było nieodpowiednie.reader.ReadToEndAsync()
Zamiast tego użyłem .var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
jest używane wtry
bloku podczas odczytu linii. Jeśli koder nie może przeanalizować podanego tekstu (tekst nie jest zakodowany za pomocą utf8), zostanie zgłoszony przez Utf8EncodingVerifier. Wyjątek został przechwycony, a następnie wiemy, że tekst nie jest utf8 i domyślnie ISO-8859-1Poniższe kody są moimi kodami Powershell do określenia, czy niektóre pliki cpp, h lub ml są kodowane za pomocą ISO-8859-1 (Latin-1) lub UTF-8 bez BOM, jeśli żaden z nich nie powinien być GB18030. Jestem Chińczykiem pracującym we Francji i MSVC zapisuje jako Latin-1 na francuskim komputerze i zapisuje jako GB na chińskim komputerze, więc pomaga mi to uniknąć problemów z kodowaniem podczas wymiany plików źródłowych między moim systemem a kolegami.
Sposób jest prosty, jeśli wszystkie znaki są między x00-x7E, ASCII, UTF-8 i Latin-1 są takie same, ale jeśli przeczytam plik inny niż ASCII przez UTF-8, znajdziemy znak specjalny pojawi się , więc spróbuj czytać z Latin-1. W Latin-1 między \ x7F a \ xAF jest puste, podczas gdy GB używa pełnego między x00-xFF, więc jeśli mam coś między nimi, nie jest to Latin-1
Kod jest napisany w PowerShell, ale używa .net, więc można go łatwo przetłumaczyć na C # lub F #
źródło
.NET nie jest zbyt pomocny, ale możesz wypróbować następujący algorytm:
Oto wezwanie:
Oto kod:
źródło
Poszukaj tutaj c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
źródło
Może się przydać
źródło