Vâng là một câu hỏi thường gặp nhất, và vấn đề này đối với tôi là mơ hồ và vì tôi không biết nhiều về nó.
Nhưng tôi muốn một cách rất chính xác để tìm một tập tin Mã hóa. Chính xác như Notepad ++.
Vâng là một câu hỏi thường gặp nhất, và vấn đề này đối với tôi là mơ hồ và vì tôi không biết nhiều về nó.
Nhưng tôi muốn một cách rất chính xác để tìm một tập tin Mã hóa. Chính xác như Notepad ++.
Câu trả lời:
Các StreamReader.CurrentEncoding
tài sản hiếm khi trả về tập tin văn bản đúng mã hóa đối với tôi. Tôi đã thành công lớn hơn trong việc xác định độ bền của tệp, bằng cách phân tích dấu thứ tự byte (BOM) của nó. Nếu tệp không có BOM, điều này không thể xác định mã hóa của tệp.
* CẬP NHẬT 4/08/2020 để bao gồm phát hiện UTF-32LE và trả về mã hóa chính xác cho 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
, thực hiện đó là những gì nhiều người sẽ muốn. Họ tạo các mã hóa mới thay vì sử dụng các Encoding.Unicode
đối tượng hiện có , vì vậy việc kiểm tra bình đẳng sẽ không thành công (điều này có thể hiếm khi xảy ra vì ví dụ: Encoding.UTF8
có thể trả về các đối tượng khác nhau), nhưng nó (1) không sử dụng định dạng UTF-7 thực sự kỳ lạ, (2) mặc định là UTF-8 nếu không tìm thấy BOM và (3) có thể được ghi đè để sử dụng một mã hóa mặc định khác.
00 00 FE FF
Encoding.UTF32
FF FE 00 00
Mã sau hoạt động tốt đối với tôi, sử dụng StreamReader
lớp:
using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
{
reader.Peek(); // you need this!
var encoding = reader.CurrentEncoding;
}
Bí quyết là sử dụng Peek
cuộc gọi, nếu không, .NET đã không thực hiện bất kỳ điều gì (và nó không đọc phần mở đầu, BOM). Tất nhiên, nếu bạn sử dụng bất kỳ ReadXXX
cuộc gọi nào khác trước khi kiểm tra mã hóa, nó cũng hoạt động.
Nếu tệp không có BOM, thì defaultEncodingIfNoBom
mã hóa sẽ được sử dụng. Cũng có một StreamReader không có phương thức quá tải này (trong trường hợp này, mã hóa Mặc định (ANSI) sẽ được sử dụng làm defaultEncodingIfNoBom), nhưng tôi khuyên bạn nên xác định những gì bạn coi là mã hóa mặc định trong ngữ cảnh của mình.
Tôi đã kiểm tra điều này thành công với các tệp có BOM cho UTF8, UTF16 / Unicode (LE & BE) và UTF32 (LE & BE). Nó không hoạt động cho 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
Tôi sẽ thử các bước sau:
1) Kiểm tra xem có Dấu đơn hàng Byte không
2) Kiểm tra xem tệp có phải là UTF8 hợp lệ không
3) Sử dụng mã "ANSI" cục bộ (ANSI do Microsoft định nghĩa)
Bước 2 hoạt động vì hầu hết các chuỗi không phải ASCII trong các mã khác UTF8 không phải là UTF8 hợp lệ.
Utf8Encoding
bạn, bạn có thể truyền vào một tham số bổ sung để xác định xem có nên đưa ra một ngoại lệ hay không hoặc nếu bạn thích tham nhũng dữ liệu im lặng.
Kiểm tra điều này.
Đây là một cổng của Mozilla Universal Charset Detector và bạn có thể sử dụng nó như thế này ...
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").
Cung cấp chi tiết triển khai cho các bước do @CodesInChaos đề xuất:
1) Kiểm tra xem có Dấu đơn hàng Byte không
2) Kiểm tra xem tệp có phải là UTF8 hợp lệ không
3) Sử dụng mã "ANSI" cục bộ (ANSI do Microsoft định nghĩa)
Bước 2 hoạt động vì hầu hết các chuỗi không phải ASCII trong các mã khác UTF8 không phải là UTF8 hợp lệ. https://stackoverflow.com/a/4522251/867248 giải thích chiến thuật chi tiết hơn.
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()
thay vì while (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
không đọc toàn bộ luồng. Tôi thấy rằng với các luồng lớn hơn, Peek()
là không đủ. Tôi đã sử dụng reader.ReadToEndAsync()
thay thế.
var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
Nó được sử dụng trong try
khối khi đọc một dòng. Nếu bộ mã hóa không phân tích được văn bản được cung cấp (văn bản không được mã hóa bằng utf8), Utf8EncodingVerifier sẽ ném ra. Ngoại lệ được giải quyết và sau đó chúng tôi biết văn bản không phải là utf8 và mặc định là ISO-8859-1
Các mã sau là mã Powershell của tôi để xác định xem một số tệp cpp hoặc h hoặc ml có đang mã hóa ISO-8859-1 (Latin-1) hoặc UTF-8 không mà không có BOM hay không, nếu không thì giả sử đó là GB18030. Tôi là người Trung Quốc làm việc tại Pháp và MSVC lưu dưới dạng Latin-1 trên máy tính Pháp và lưu dưới dạng GB trên máy tính Trung Quốc, vì vậy điều này giúp tôi tránh được sự cố mã hóa khi trao đổi tệp nguồn giữa hệ thống của tôi và đồng nghiệp.
Cách làm rất đơn giản, nếu tất cả các ký tự nằm giữa x00-x7E, ASCII, UTF-8 và Latin-1 đều giống nhau, nhưng nếu tôi đọc tệp không phải ASCII bởi UTF-8, chúng tôi sẽ thấy ký tự đặc biệt hiển thị , vì vậy hãy cố gắng đọc bằng Latin-1. Trong Latinh-1, giữa \ x7F và \ xAF trống, trong khi GB sử dụng đầy đủ giữa x00-xFF, vì vậy nếu tôi có bất kỳ cái nào giữa hai, nó không phải là Latinh-1
Mã được viết bằng PowerShell, nhưng sử dụng .net nên dễ dàng được dịch sang C # hoặ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 không hữu ích lắm, nhưng bạn có thể thử thuật toán sau:
Đây là cuộc gọi:
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");
Đây là mã:
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; //
}
}
Hãy tìm c # ở đây
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();
}