Да - самый частый вопрос, и этот вопрос для меня непонятен, так как я мало о нем знаю.
Но я хотел бы очень точный способ найти файл Encoding. Такой точный, как Notepad ++.
Да - самый частый вопрос, и этот вопрос для меня непонятен, так как я мало о нем знаю.
Но я хотел бы очень точный способ найти файл Encoding. Такой точный, как Notepad ++.
Ответы:
StreamReader.CurrentEncoding
Свойство редко возвращает правильный текстовый файл , кодирующий для меня. Я добился большего успеха в определении порядка байтов файла, анализируя его метку порядка байтов (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, если спецификация не найдена, и (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 ничего не сделал (и не прочитал преамбулу, спецификацию). Конечно, если вы используете любой другой ReadXXX
вызов перед проверкой кодировки, он тоже работает.
Если у файла нет спецификации, то defaultEncodingIfNoBom
будет использоваться кодировка. Существует также StreamReader без этого метода перегрузки (в этом случае кодировка по умолчанию (ANSI) будет использоваться как defaultEncodingIfNoBom), но я рекомендую определить, что вы считаете кодировкой по умолчанию в вашем контексте.
Я успешно протестировал это с файлами со спецификацией для 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) Проверьте, есть ли отметка порядка байтов
2) Убедитесь, что файл действителен UTF8
3) Используйте локальную кодовую страницу "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) Проверьте, есть ли отметка порядка байтов
2) Убедитесь, что файл действителен UTF8
3) Используйте локальную кодовую страницу "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 без спецификации, если ни то, ни другое, тогда предположите, что это GB18030. Я китаец, работающий во Франции, и MSVC сохраняет как Latin-1 на французском компьютере и сохраняет как GB на китайском компьютере, так что это помогает мне избежать проблем с кодированием при обмене исходными файлами между моей системой и моими коллегами.
Способ прост, если все символы находятся между x00-x7E, ASCII, UTF-8 и Latin-1 все одинаковы, но если я прочитаю файл не ASCII с помощью UTF-8, мы найдем специальный символ , поэтому попробуйте читать с помощью Latin-1. В Latin-1 между \ x7F и \ xAF пусто, в то время как GB использует full между x00-xFF, поэтому, если у меня есть что-то между ними, это не Latin-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; //
}
}
Ищите здесь С #
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();
}