@Oded: Kutipan "Metode getEncoding () akan mengembalikan encoding yang telah disiapkan (baca JavaDoc) untuk streaming. Metode tersebut tidak akan menebak encoding untuk Anda.".
Fábio Antunes
2
Untuk beberapa bacaan latar belakang, joelonsoftware.com/articles/Unicode.html adalah bacaan yang bagus. Jika ada satu hal yang perlu Anda ketahui tentang teks, itu adalah tidak ada yang namanya teks biasa.
Martijn
Jawaban:
155
The StreamReader.CurrentEncodingproperti jarang mengembalikan file teks yang benar pengkodean bagi saya. Saya lebih berhasil dalam menentukan keuletan file, dengan menganalisis byte order mark (BOM). Jika file tidak memiliki BOM, ini tidak dapat menentukan pengkodean file.
* DIPERBARUI 4/08/2020 untuk menyertakan deteksi UTF-32LE dan mengembalikan pengkodean yang benar untuk 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. Ini bekerja untuk saya juga (sedangkan detectEncodingFromByteOrderMarks tidak). Saya menggunakan "FileStream baru (nama file, FileMode.Open, FileAccess.Read)" untuk menghindari IOException karena file tersebut hanya dapat dibaca.
Polyfun
56
File UTF-8 bisa tanpa BOM, dalam hal ini akan mengembalikan ASCII dengan tidak benar.
pengguna626528
3
Jawaban ini salah. Melihat sumber referensi untuk StreamReader, implementasi itulah yang lebih banyak orang akan ingin. Mereka membuat pengkodean baru daripada menggunakan Encoding.Unicodeobjek yang sudah ada , sehingga pemeriksaan kesetaraan akan gagal (yang mungkin jarang terjadi karena, misalnya, Encoding.UTF8dapat mengembalikan objek yang berbeda), tetapi (1) tidak menggunakan format UTF-7 yang benar-benar aneh, (2) default ke UTF-8 jika tidak ada BOM yang ditemukan, dan (3) dapat diganti untuk menggunakan encoding default yang berbeda.
hanggar
2
saya lebih sukses dengan StreamReader baru (nama file, benar) .CurrentEncoding
Benoit
4
Ada kesalahan mendasar dalam kode; saat Anda mendeteksi tanda tangan UTF32 big-endian ( ), Anda mengembalikan sistem yang disediakan , yang merupakan encoding little-endian (seperti disebutkan di sini ). Dan juga, seperti dicatat oleh @Nyerguds, Anda masih belum mencari UTF32LE, yang memiliki tanda tangan (menurut en.wikipedia.org/wiki/Byte_order_mark ). Seperti yang dicatat oleh pengguna tersebut, karena ini sedang berlangsung, pemeriksaan tersebut harus dilakukan sebelum pemeriksaan 2-byte. 00 00 FE FFEncoding.UTF32FF FE 00 00
Glenn Slayden
44
Kode berikut berfungsi dengan baik untuk saya, menggunakan StreamReaderkelas:
using (var reader =newStreamReader(fileName, defaultEncodingIfNoBom,true)){
reader.Peek();// you need this!var encoding = reader.CurrentEncoding;}
Triknya adalah menggunakan Peekpanggilan, jika tidak, .NET belum melakukan apa-apa (dan belum membaca pembukaan, BOM). Tentu saja, jika Anda menggunakan ReadXXXpanggilan lain sebelum memeriksa encoding, itu juga berfungsi.
Jika file tidak memiliki BOM, maka defaultEncodingIfNoBompengkodean akan digunakan. Ada juga StreamReader tanpa metode kelebihan beban ini (dalam hal ini, pengkodean Default (ANSI) akan digunakan sebagai defaultEncodingIfNoBom), tetapi saya merekomendasikan untuk menentukan apa yang Anda anggap sebagai pengkodean default dalam konteks Anda.
Saya telah menguji ini dengan sukses dengan file dengan BOM untuk UTF8, UTF16 / Unicode (LE & BE) dan UTF32 (LE & BE). Ini tidak bekerja untuk UTF7.
Saya mendapatkan kembali apa yang ditetapkan sebagai pengkodean default. Mungkinkah saya melewatkan sesuatu?
Ram
1
@DRAM - ini dapat terjadi jika file tidak memiliki BOM
Simon Mourier
Terima kasih @Simon Mourier. Saya tidak berharap pdf / file apa pun saya tidak akan memiliki bom. Link stackoverflow.com/questions/4520184/… ini mungkin berguna bagi seseorang yang mencoba mendeteksi tanpa bom.
Ram
1
Di PowerShell saya harus menjalankan $ reader.close (), atau terkunci dari penulisan. 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 Ini tidak berfungsi jika pengkodean file adalahUTF-8 without BOM
Ozkan
11
Saya akan mencoba langkah-langkah berikut:
1) Periksa apakah ada Byte Order Mark
2) Periksa apakah file tersebut UTF8 valid
3) Gunakan halaman kode "ANSI" lokal (ANSI seperti yang didefinisikan oleh Microsoft)
Langkah 2 berfungsi karena sebagian besar urutan non ASCII dalam halaman kode selain UTF8 bukan UTF8 yang valid.
Sepertinya ini jawaban yang lebih benar, karena jawaban lain tidak berhasil untuk saya. Seseorang dapat melakukannya dengan File.OpenRead dan .Read-ing beberapa byte pertama dari file tersebut.
user420667
1
Langkah 2 adalah sejumlah pekerjaan pemrograman untuk memeriksa pola bit.
Nyerguds
1
Saya tidak yakin decoding benar-benar melempar pengecualian, atau jika itu hanya menggantikan urutan yang tidak dikenali dengan '?'. Lagipula, aku pergi dengan menulis kelas memeriksa pola sedikit.
Nyerguds
3
Saat Anda membuat sebuah instance, Utf8EncodingAnda dapat mengirimkan parameter tambahan yang menentukan apakah pengecualian harus dilemparkan atau jika Anda lebih suka kerusakan data diam.
CodesInChaos
1
Saya suka jawaban ini. Sebagian besar pengkodean (mungkin 99% dari kasus penggunaan Anda) akan berupa UTF-8 atau ANSI (halaman kode Windows 1252). Anda dapat memeriksa apakah string berisi karakter pengganti (0xFFFD) untuk menentukan apakah encoding gagal.
Ok jika Anda khawatir tentang lisensinya maka Anda dapat menggunakan yang ini. Berlisensi sebagai MIT dan Anda dapat menggunakannya untuk perangkat lunak sumber terbuka dan sumber tertutup. nuget.org/packages/SimpleHelpers.FileEncoding
Alexei Agüero Alba
Lisensi adalah MPL dengan opsi 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
Tampaknya garpu ini saat ini yang paling aktif dan memiliki paket nuget UDE.Netstandard. github.com/yinyue200/ude
jbtule
perpustakaan yang sangat berguna, mengatasi banyak pengkodean yang berbeda dan tidak biasa! tank!
mshakurov
6
Memberikan detail implementasi untuk langkah-langkah yang diusulkan oleh @CodesInChaos:
1) Periksa apakah ada Byte Order Mark
2) Periksa apakah file tersebut UTF8 valid
3) Gunakan halaman kode "ANSI" lokal (ANSI seperti yang didefinisikan oleh Microsoft)
Langkah 2 berfungsi karena sebagian besar urutan non ASCII dalam halaman kode selain UTF8 bukan UTF8 yang valid. https://stackoverflow.com/a/4522251/867248 menjelaskan taktik ini lebih detail.
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();...
Terima kasih! Ini terpecahkan untuk saya. Tapi saya lebih suka menggunakan reader.Peek() daripada while (!reader.EndOfStream) { var line = reader.ReadLine(); }
Harison Silva
reader.Peek()tidak membaca keseluruhan aliran. Saya menemukan bahwa dengan aliran yang lebih besar, Peek()tidak memadai. Saya menggunakan reader.ReadToEndAsync()sebagai gantinya.
Gary Pendlebury
Dan apakah Utf8EncodingVerifier itu?
Peter Moore
1
@PeterMoore Ini adalah pengkodean untuk utf8, var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());Ini digunakan di tryblok saat membaca baris. Jika pembuat enkode gagal mengurai teks yang disediakan (teks tidak dienkode dengan utf8), Utf8EncodingVerifier akan melempar. Pengecualian ditangkap dan kita kemudian tahu teksnya bukan utf8, dan default ke ISO-8859-1
Berthier Lemieux
2
Kode berikut adalah kode Powershell saya untuk menentukan apakah beberapa file cpp atau h atau ml dikodekan dengan ISO-8859-1 (Latin-1) atau UTF-8 tanpa BOM, jika tidak ada maka anggaplah itu GB18030. Saya orang China yang bekerja di Prancis dan MSVC menyimpan sebagai Latin-1 di komputer Prancis dan menyimpan sebagai GB di komputer China jadi ini membantu saya menghindari masalah encoding saat melakukan pertukaran file sumber antara sistem saya dan kolega saya.
Caranya sederhana, jika semua karakter antara x00-x7E, ASCII, UTF-8 dan Latin-1 semuanya sama, tetapi jika saya membaca file non ASCII oleh UTF-8, kita akan menemukan karakter khusus muncul , jadi cobalah membaca dengan Latin-1. Dalam Latin-1, antara \ x7F dan \ xAF kosong, sedangkan GB menggunakan penuh antara x00-xFF jadi jika saya mendapatkan di antara keduanya, itu bukan Latin-1
Kode tersebut ditulis dalam PowerShell, tetapi menggunakan .net sehingga mudah untuk diterjemahkan ke dalam C # atau F #
.NET tidak terlalu membantu, tetapi Anda dapat mencoba algoritme berikut:
mencoba untuk menemukan pengkodean dengan BOM (tanda urutan byte) ... sangat mungkin tidak dapat ditemukan
coba parsing menjadi penyandiaksaraan yang berbeda
Inilah panggilannya:
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");
Ini kodenya:
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();}
Jawaban:
The
StreamReader.CurrentEncoding
properti jarang mengembalikan file teks yang benar pengkodean bagi saya. Saya lebih berhasil dalam menentukan keuletan file, dengan menganalisis byte order mark (BOM). Jika file tidak memiliki BOM, ini tidak dapat menentukan pengkodean file.* DIPERBARUI 4/08/2020 untuk menyertakan deteksi UTF-32LE dan mengembalikan pengkodean yang benar untuk UTF-32BE
sumber
StreamReader
, implementasi itulah yang lebih banyak orang akan ingin. Mereka membuat pengkodean baru daripada menggunakanEncoding.Unicode
objek yang sudah ada , sehingga pemeriksaan kesetaraan akan gagal (yang mungkin jarang terjadi karena, misalnya,Encoding.UTF8
dapat mengembalikan objek yang berbeda), tetapi (1) tidak menggunakan format UTF-7 yang benar-benar aneh, (2) default ke UTF-8 jika tidak ada BOM yang ditemukan, dan (3) dapat diganti untuk menggunakan encoding default yang berbeda.00 00 FE FF
Encoding.UTF32
FF FE 00 00
Kode berikut berfungsi dengan baik untuk saya, menggunakan
StreamReader
kelas:Triknya adalah menggunakan
Peek
panggilan, jika tidak, .NET belum melakukan apa-apa (dan belum membaca pembukaan, BOM). Tentu saja, jika Anda menggunakanReadXXX
panggilan lain sebelum memeriksa encoding, itu juga berfungsi.Jika file tidak memiliki BOM, maka
defaultEncodingIfNoBom
pengkodean akan digunakan. Ada juga StreamReader tanpa metode kelebihan beban ini (dalam hal ini, pengkodean Default (ANSI) akan digunakan sebagai defaultEncodingIfNoBom), tetapi saya merekomendasikan untuk menentukan apa yang Anda anggap sebagai pengkodean default dalam konteks Anda.Saya telah menguji ini dengan sukses dengan file dengan BOM untuk UTF8, UTF16 / Unicode (LE & BE) dan UTF32 (LE & BE). Ini tidak bekerja untuk UTF7.
sumber
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
Saya akan mencoba langkah-langkah berikut:
1) Periksa apakah ada Byte Order Mark
2) Periksa apakah file tersebut UTF8 valid
3) Gunakan halaman kode "ANSI" lokal (ANSI seperti yang didefinisikan oleh Microsoft)
Langkah 2 berfungsi karena sebagian besar urutan non ASCII dalam halaman kode selain UTF8 bukan UTF8 yang valid.
sumber
Utf8Encoding
Anda dapat mengirimkan parameter tambahan yang menentukan apakah pengecualian harus dilemparkan atau jika Anda lebih suka kerusakan data diam.Periksa ini.
UDE
Ini adalah port Mozilla Universal Charset Detector dan Anda dapat menggunakannya seperti ini ...
sumber
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").
Memberikan detail implementasi untuk langkah-langkah yang diusulkan oleh @CodesInChaos:
1) Periksa apakah ada Byte Order Mark
2) Periksa apakah file tersebut UTF8 valid
3) Gunakan halaman kode "ANSI" lokal (ANSI seperti yang didefinisikan oleh Microsoft)
Langkah 2 berfungsi karena sebagian besar urutan non ASCII dalam halaman kode selain UTF8 bukan UTF8 yang valid. https://stackoverflow.com/a/4522251/867248 menjelaskan taktik ini lebih detail.
sumber
reader.Peek()
daripadawhile (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
tidak membaca keseluruhan aliran. Saya menemukan bahwa dengan aliran yang lebih besar,Peek()
tidak memadai. Saya menggunakanreader.ReadToEndAsync()
sebagai gantinya.var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
Ini digunakan ditry
blok saat membaca baris. Jika pembuat enkode gagal mengurai teks yang disediakan (teks tidak dienkode dengan utf8), Utf8EncodingVerifier akan melempar. Pengecualian ditangkap dan kita kemudian tahu teksnya bukan utf8, dan default ke ISO-8859-1Kode berikut adalah kode Powershell saya untuk menentukan apakah beberapa file cpp atau h atau ml dikodekan dengan ISO-8859-1 (Latin-1) atau UTF-8 tanpa BOM, jika tidak ada maka anggaplah itu GB18030. Saya orang China yang bekerja di Prancis dan MSVC menyimpan sebagai Latin-1 di komputer Prancis dan menyimpan sebagai GB di komputer China jadi ini membantu saya menghindari masalah encoding saat melakukan pertukaran file sumber antara sistem saya dan kolega saya.
Caranya sederhana, jika semua karakter antara x00-x7E, ASCII, UTF-8 dan Latin-1 semuanya sama, tetapi jika saya membaca file non ASCII oleh UTF-8, kita akan menemukan karakter khusus muncul , jadi cobalah membaca dengan Latin-1. Dalam Latin-1, antara \ x7F dan \ xAF kosong, sedangkan GB menggunakan penuh antara x00-xFF jadi jika saya mendapatkan di antara keduanya, itu bukan Latin-1
Kode tersebut ditulis dalam PowerShell, tetapi menggunakan .net sehingga mudah untuk diterjemahkan ke dalam C # atau F #
sumber
.NET tidak terlalu membantu, tetapi Anda dapat mencoba algoritme berikut:
Inilah panggilannya:
Ini kodenya:
sumber
Cari di sini untuk c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
sumber
Semoga bermanfaat
sumber