Ya adalah pertanyaan yang paling sering, dan masalah ini tidak jelas bagi saya dan karena saya tidak tahu banyak tentangnya.
Tetapi saya ingin cara yang sangat tepat untuk menemukan Encoding file. Setepat Notepad ++.
Ya adalah pertanyaan yang paling sering, dan masalah ini tidak jelas bagi saya dan karena saya tidak tahu banyak tentangnya.
Tetapi saya ingin cara yang sangat tepat untuk menemukan Encoding file. Setepat Notepad ++.
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
/// <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>
public static Encoding GetEncoding(string filename)
{
// Read the BOM
var bom = new byte[4];
using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
file.Read(bom, 0, 4);
}
// Analyze the BOM
if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0) return Encoding.UTF32; //UTF-32LE
if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return new 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 ASCII
return Encoding.ASCII;
}
StreamReader
, implementasi itulah yang lebih banyak orang akan ingin. Mereka membuat pengkodean baru daripada menggunakan Encoding.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:
using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
{
reader.Peek(); // you need this!
var encoding = reader.CurrentEncoding;
}
Triknya adalah menggunakan Peek
panggilan, jika tidak, .NET belum melakukan apa-apa (dan belum membaca pembukaan, BOM). Tentu saja, jika Anda menggunakan ReadXXX
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.
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.
Utf8Encoding
Anda dapat mengirimkan parameter tambahan yang menentukan apakah pengecualian harus dilemparkan atau jika Anda lebih suka kerusakan data diam.
Periksa ini.
Ini adalah port Mozilla Universal Charset Detector dan Anda dapat menggunakannya seperti ini ...
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.");
}
}
}
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.
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" codepage
public string DetectFileEncoding(Stream fileStream)
{
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
using (var reader = new StreamReader(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]
public void Test1()
{
Stream fs = File.OpenRead(@".\TestData\TextFile_ansi.csv");
var detectedEncoding = DetectFileEncoding(fs);
using (var reader = new StreamReader(fs, Encoding.GetEncoding(detectedEncoding)))
{
// Consume your file
var line = reader.ReadLine();
...
reader.Peek()
daripada while (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
tidak membaca keseluruhan aliran. Saya menemukan bahwa dengan aliran yang lebih besar, Peek()
tidak memadai. Saya menggunakan reader.ReadToEndAsync()
sebagai gantinya.
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
Ini digunakan di try
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-1
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 #
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in Get-ChildItem .\ -Recurse -include *.cpp,*.h, *.ml) {
$openUTF = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::UTF8)
$contentUTF = $openUTF.ReadToEnd()
[regex]$regex = '�'
$c=$regex.Matches($contentUTF).count
$openUTF.Close()
if ($c -ne 0) {
$openLatin1 = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('ISO-8859-1'))
$contentLatin1 = $openLatin1.ReadToEnd()
$openLatin1.Close()
[regex]$regex = '[\x7F-\xAF]'
$c=$regex.Matches($contentLatin1).count
if ($c -eq 0) {
[System.IO.File]::WriteAllLines($i, $contentLatin1, $Utf8NoBomEncoding)
$i.FullName
}
else {
$openGB = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('GB18030'))
$contentGB = $openGB.ReadToEnd()
$openGB.Close()
[System.IO.File]::WriteAllLines($i, $contentGB, $Utf8NoBomEncoding)
$i.FullName
}
}
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
.NET tidak terlalu membantu, tetapi Anda dapat mencoba algoritme berikut:
Inilah panggilannya:
var encoding = FileHelper.GetEncoding(filePath);
if (encoding == null)
throw new Exception("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");
Ini kodenya:
public class FileHelper
{
/// <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>
public static Encoding GetEncoding(string filename)
{
var encodingByBOM = GetEncodingByBOM(filename);
if (encodingByBOM != null)
return encodingByBOM;
// BOM not found :(, so try to parse characters into several encodings
var 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;
return null; // 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>
private static Encoding GetEncodingByBOM(string filename)
{
// Read the BOM
var byteOrderMark = new byte[4];
using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
file.Read(byteOrderMark, 0, 4);
}
// Analyze the BOM
if (byteOrderMark[0] == 0x2b && byteOrderMark[1] == 0x2f && byteOrderMark[2] == 0x76) return Encoding.UTF7;
if (byteOrderMark[0] == 0xef && byteOrderMark[1] == 0xbb && byteOrderMark[2] == 0xbf) return Encoding.UTF8;
if (byteOrderMark[0] == 0xff && byteOrderMark[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
if (byteOrderMark[0] == 0xfe && byteOrderMark[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
if (byteOrderMark[0] == 0 && byteOrderMark[1] == 0 && byteOrderMark[2] == 0xfe && byteOrderMark[3] == 0xff) return Encoding.UTF32;
return null; // no BOM found
}
private static Encoding GetEncodingByParsing(string filename, Encoding encoding)
{
var encodingVerifier = Encoding.GetEncoding(encoding.BodyName, new EncoderExceptionFallback(), new DecoderExceptionFallback());
try
{
using (var textReader = new StreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks: true))
{
while (!textReader.EndOfStream)
{
textReader.ReadLine(); // in order to increment the stream position
}
// all text parsed ok
return textReader.CurrentEncoding;
}
}
catch (Exception ex) { }
return null; //
}
}
Cari di sini untuk c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
string path = @"path\to\your\file.ext";
using (StreamReader sr = new StreamReader(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();
}