Так, це найчастіше запитання, і це питання для мене розпливчасте, тому що я мало про це знаю.
Але я хотів би дуже точний спосіб знайти файли кодування. Настільки точний, як 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) Перевірте, чи є марка порядку байтів
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 (латинська-1) або UTF-8 без BOM, якщо жодне з них не вважає, що це GB18030. Я є китайцем, який працює у Франції, а MSVC економить як латинський-1 на французькому комп’ютері і економить як ГБ на китайському комп’ютері, тому це допомагає мені уникнути проблеми кодування під час обміну вихідними файлами між моєю системою та моїми колегами.
Шлях простий, якщо всі символи знаходяться між x00-x7E, ASCII, UTF-8 та Latin-1 - все одно, але якщо я прочитаю файл, що не є ASCII UTF-8, ми знайдемо спеціальний символ з'явиться , тому спробуйте читати з латиниці-1. У латинській-1 між \ x7F та \ xAF порожньо, тоді як ГБ використовує повний розмір між 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();
}