ใช่เป็นคำถามที่พบบ่อยที่สุดและเรื่องนี้คลุมเครือสำหรับฉันและเนื่องจากฉันไม่ค่อยรู้เรื่องนี้มากนัก
แต่ฉันต้องการวิธีที่แม่นยำมากในการค้นหาการเข้ารหัสไฟล์ แม่นยำเหมือน Notepad ++
ใช่เป็นคำถามที่พบบ่อยที่สุดและเรื่องนี้คลุมเครือสำหรับฉันและเนื่องจากฉันไม่ค่อยรู้เรื่องนี้มากนัก
แต่ฉันต้องการวิธีที่แม่นยำมากในการค้นหาการเข้ารหัสไฟล์ แม่นยำเหมือน Notepad ++
คำตอบ:
StreamReader.CurrentEncoding
คุณสมบัติไม่ค่อยผลตอบแทนการเข้ารหัสไฟล์ข้อความที่ถูกต้องสำหรับผม ฉันประสบความสำเร็จมากขึ้นในการพิจารณาความทนทานของไฟล์โดยการวิเคราะห์เครื่องหมายลำดับไบต์ (BOM) หากไฟล์ไม่มี BOM จะไม่สามารถกำหนดการเข้ารหัสของไฟล์ได้
* อัปเดตเมื่อ 4/08/2020 เพื่อรวมการตรวจจับ UTF-32LE และส่งคืนการเข้ารหัสที่ถูกต้องสำหรับ 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
ว่าการดำเนินการเป็นสิ่งที่คนอื่น ๆ จะต้องการ พวกเขาทำการเข้ารหัสใหม่แทนที่จะใช้Encoding.Unicode
วัตถุที่มีอยู่ดังนั้นการตรวจสอบความเท่าเทียมกันจะล้มเหลว (ซึ่งอาจไม่ค่อยเกิดขึ้นอยู่ดีเพราะเช่นEncoding.UTF8
สามารถส่งคืนวัตถุที่แตกต่างกัน) แต่ (1) ไม่ได้ใช้รูปแบบ UTF-7 ที่แปลกจริงๆ (2) ค่าเริ่มต้นเป็น UTF-8 หากไม่พบ BOM และ (3) สามารถแทนที่เพื่อใช้การเข้ารหัสเริ่มต้นอื่นได้
00 00 FE FF
Encoding.UTF32
FF FE 00 00
รหัสต่อไปนี้ใช้งานได้ดีสำหรับฉันโดยใช้StreamReader
คลาส:
using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
{
reader.Peek(); // you need this!
var encoding = reader.CurrentEncoding;
}
เคล็ดลับคือการใช้การPeek
โทรมิฉะนั้น. NET ยังไม่ได้ทำอะไรเลย (และยังไม่ได้อ่านคำนำหน้า BOM) แน่นอนว่าหากคุณใช้การReadXXX
โทรอื่น ๆก่อนที่จะตรวจสอบการเข้ารหัสก็ใช้ได้เช่นกัน
หากไฟล์ไม่มี BOM ระบบdefaultEncodingIfNoBom
จะใช้การเข้ารหัส นอกจากนี้ยังมี StreamReader ที่ไม่มีวิธีโอเวอร์โหลดนี้ (ในกรณีนี้การเข้ารหัสเริ่มต้น (ANSI) จะใช้เป็น defaultEncodingIfNoBom) แต่ฉันขอแนะนำให้กำหนดสิ่งที่คุณพิจารณาการเข้ารหัสเริ่มต้นในบริบทของคุณ
ฉันได้ทดสอบสิ่งนี้สำเร็จด้วยไฟล์ที่มี BOM สำหรับ UTF8, UTF16 / Unicode (LE & BE) และ UTF32 (LE & BE) มันใช้ไม่ได้กับ 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
ฉันจะลองทำตามขั้นตอนต่อไปนี้:
1) ตรวจสอบว่ามี Byte Order Mark หรือไม่
2) ตรวจสอบว่าไฟล์เป็น UTF8 ที่ถูกต้องหรือไม่
3) ใช้ codepage "ANSI" ในเครื่อง (ANSI ตามที่ Microsoft กำหนด)
ขั้นตอนที่ 2 ทำงานได้เนื่องจากลำดับ ASCII ส่วนใหญ่ที่ไม่ใช่ในโค้ดเพจอื่น ๆ ที่ UTF8 ไม่ใช่ UTF8 ที่ถูกต้อง
Utf8Encoding
คุณสามารถส่งผ่านพารามิเตอร์พิเศษที่กำหนดว่าควรทิ้งข้อยกเว้นหรือหากคุณต้องการให้ข้อมูลเสียหายแบบไม่โต้ตอบ
ตรวจสอบสิ่งนี้
นี่คือพอร์ตของ Mozilla Universal Charset Detector และคุณสามารถใช้มันได้แบบนี้ ...
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").
ให้รายละเอียดการใช้งานสำหรับขั้นตอนที่เสนอโดย @CodesInChaos:
1) ตรวจสอบว่ามี Byte Order Mark หรือไม่
2) ตรวจสอบว่าไฟล์เป็น UTF8 ที่ถูกต้องหรือไม่
3) ใช้ codepage "ANSI" ในเครื่อง (ANSI ตามที่ Microsoft กำหนด)
ขั้นตอนที่ 2 ทำงานได้เนื่องจากลำดับ ASCII ส่วนใหญ่ที่ไม่ใช่ในโค้ดเพจอื่น ๆ ที่ UTF8 ไม่ใช่ UTF8 ที่ถูกต้อง https://stackoverflow.com/a/4522251/867248อธิบายกลยุทธ์ในรายละเอียดเพิ่มเติม
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()
แทน while (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
ไม่อ่านสตรีมทั้งหมด ฉันพบว่าด้วยลำธารที่ใหญ่กว่าPeek()
นั้นไม่เพียงพอ ฉันใช้reader.ReadToEndAsync()
แทน
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
ซึ่งใช้ในtry
บล็อกเมื่ออ่านบรรทัด หากตัวเข้ารหัสไม่สามารถแยกวิเคราะห์ข้อความที่ให้มา (ข้อความไม่ได้เข้ารหัสด้วย utf8) Utf8EncodingVerifier จะโยน ข้อยกเว้นถูกจับแล้วเรารู้ว่าข้อความไม่ใช่ utf8 และเริ่มต้นเป็น ISO-8859-1
รหัสต่อไปนี้เป็นรหัส Powershell ของฉันเพื่อตรวจสอบว่าไฟล์ cpp หรือ h หรือ ml บางไฟล์เข้ารหัสด้วย ISO-8859-1 (Latin-1) หรือ UTF-8 โดยไม่มี BOM ถ้าไม่สมมติว่าเป็น GB18030 ฉันเป็นคนจีนที่ทำงานในฝรั่งเศสและ MSVC บันทึกเป็นภาษาละติน -1 บนคอมพิวเตอร์ฝรั่งเศสและบันทึกเป็น GB ในคอมพิวเตอร์ภาษาจีนดังนั้นสิ่งนี้จะช่วยให้ฉันหลีกเลี่ยงปัญหาการเข้ารหัสเมื่อมีการแลกเปลี่ยนไฟล์ต้นฉบับระหว่างระบบของฉันกับเพื่อนร่วมงาน
วิธีง่ายๆคือถ้าอักขระทั้งหมดอยู่ระหว่าง x00-x7E, ASCII, UTF-8 และ Latin-1 จะเหมือนกันหมด แต่ถ้าฉันอ่านไฟล์ที่ไม่ใช่ ASCII โดย UTF-8 เราจะพบว่าอักขระพิเศษ แสดงขึ้น ดังนั้นพยายามอ่านด้วยภาษาละติน -1 ในภาษาลาติน -1 ระหว่าง \ x7F และ \ xAF ว่างเปล่าในขณะที่ GB ใช้เต็มระหว่าง x00-xFF ดังนั้นถ้าฉันมีระหว่างทั้งสองจะไม่ใช่ภาษาละติน -1
โค้ดนี้เขียนใน PowerShell แต่ใช้. net จึงแปลเป็น C # หรือ 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 ไม่มีประโยชน์มากนัก แต่คุณสามารถลองใช้อัลกอริทึมต่อไปนี้:
นี่คือการโทร:
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");
นี่คือรหัส:
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; //
}
}
มองหา 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();
}
มันอาจมีประโยชน์
string path = @"address/to/the/file.extension";
using (StreamReader sr = new StreamReader(path))
{
Console.WriteLine(sr.CurrentEncoding);
}