Как определить кодировку символов текстового файла?
Я пытаюсь определить, какая кодировка символов используется в моем файле.
Я пытаюсь с этим кодом получить стандартную кодировку
public static Encoding GetFileEncoding(string srcFile)
{
// *** Use Default of Encoding.Default (Ansi CodePage)
Encoding enc = Encoding.Default;
// *** Detect byte order mark if any - otherwise assume default
byte[] buffer = new byte[5];
FileStream file = new FileStream(srcFile, FileMode.Open);
file.Read(buffer, 0, 5);
file.Close();
if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
enc = Encoding.UTF8;
else if (buffer[0] == 0xfe && buffer[1] == 0xff)
enc = Encoding.Unicode;
else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
enc = Encoding.UTF32;
else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
enc = Encoding.UTF7;
else if (buffer[0] == 0xFE && buffer[1] == 0xFF)
// 1201 unicodeFFFE Unicode (Big-Endian)
enc = Encoding.GetEncoding(1201);
else if (buffer[0] == 0xFF && buffer[1] == 0xFE)
// 1200 utf-16 Unicode
enc = Encoding.GetEncoding(1200);
return enc;
}
Мои пять первых байтов - это 60, 118, 56, 46 и 49.
Есть ли диаграмма, которая показывает, какая кодировка соответствует этим пяти первым байтам?
9 ответов
Вы не можете зависеть от файла, имеющего спецификацию. UTF-8 этого не требует. И кодировки не в Юникоде даже не имеют спецификации. Однако существуют и другие способы обнаружения кодировки.
UTF-32
Спецификация: 00 00 FE FF (для BE) или FF FE 00 00 (для LE).
Но UTF-32 легко обнаружить даже без спецификации. Это связано с тем, что диапазон кодовых точек Unicode ограничен U+10FFFF, и, следовательно, блоки UTF-32 всегда имеют шаблон 00 {00-10} xx xx (для BE) или xx xx {00-10} 00 (для LE), Если длина данных кратна 4 и соответствует одному из этих шаблонов, можно смело предположить, что это UTF-32. Ложные срабатывания практически невозможны из-за редкости 00 байтов в байтово-ориентированных кодировках.
US-ASCII
Нет спецификации, но она вам не нужна. ASCII может быть легко идентифицирован по отсутствию байтов в диапазоне 80-FF.
UTF-8,
Спецификация EF BB BF. Но на это нельзя полагаться. Многие файлы UTF-8 не имеют спецификации, особенно если они созданы в системах, отличных от Windows.
Но вы можете смело предположить, что если файл проверяется как UTF-8, то это UTF-8. Ложные срабатывания редки.
В частности, учитывая, что данные не являются ASCII, частота ложноположительных результатов для двухбайтовой последовательности составляет всего 3,9% (1920/49152). Для 7-байтовой последовательности это менее 1%. Для 12-байтовой последовательности это менее 0,1%. Для 24-байтовой последовательности это меньше, чем 1 на миллион.
UTF-16
Спецификация: FE FF (для BE) или FF FE (для LE). Обратите внимание, что спецификация UTF-16LE находится в начале спецификации UTF-32LE, поэтому сначала проверьте UTF-32.
Если у вас есть файл, который состоит в основном из символов ISO-8859-1, то наличие половины байтов файла, равного 00, также будет сильным показателем UTF-16.
В противном случае единственный надежный способ распознавания UTF-16 без спецификации - это поиск суррогатных пар (D[8-B]xx D[CF]xx), но символы, отличные от BMP, используются слишком редко, чтобы сделать этот подход практичным,
XML
Если ваш файл начинается с байтов 3C 3F 78 6D 6C (т. Е. Символов ASCII " Xml"), ищите encoding=
декларация. Если присутствует, используйте эту кодировку. Если отсутствует, предположим, что UTF-8 является кодировкой XML по умолчанию.
Если вам нужна поддержка EBCDIC, также ищите эквивалентную последовательность 4C 6F A7 94 93.
В общем, если у вас есть формат файла, который содержит объявление кодировки, тогда ищите это объявление, а не пытайтесь угадать кодировку.
Ни один из вышеперечисленных
Существуют сотни других кодировок, которые требуют больше усилий для обнаружения. Я рекомендую попробовать детектор кодировки Mozilla или его порт.NET.
Разумный дефолт
Если вы исключили кодировки UTF и не имеете декларации кодирования или статистического обнаружения, указывающего на другую кодировку, предположите ISO-8859-1 или тесно связанную Windows-1252. (Обратите внимание, что новейший стандарт HTML требует, чтобы объявление "ISO-8859-1" интерпретировалось как Windows-1252.) Является кодовой страницей Windows по умолчанию для английского языка (и других популярных языков, таких как испанский, португальский, немецкий и французский), это наиболее часто встречающаяся кодировка, кроме UTF-8.
Если вы хотите найти "простое" решение, вам может пригодиться этот класс, который я собрал:
http://www.architectshack.com/TextFileEncodingDetector.ashx
Сначала выполняется автоматическое определение спецификации, а затем делается попытка провести различие между кодировками Unicode без спецификации и другой кодировкой по умолчанию (обычно Windows-1252, неправильно обозначенной как Encoding.ASCII в.Net).
Как отмечалось выше, "более тяжелое" решение, включающее NCharDet или MLang, может быть более подходящим, и, как я отмечаю на обзорной странице этого класса, лучше всего обеспечить некоторую форму интерактивности с пользователем, если это вообще возможно, потому что там просто невозможно 100% обнаружение!
Несколько ответов здесь, но никто не разместил полезный код.
Вот мой код, который обнаруживает все кодировки, которые Microsoft обнаруживает в Framework 4 в классе StreamReader.
Очевидно, что вы должны вызывать эту функцию сразу после открытия потока, прежде чем читать что-либо еще из потока, потому что спецификация - это первые байты в потоке.
Эта функция требует потока, который может искать (например, FileStream). Если у вас есть Поток, который не может искать, вы должны написать более сложный код, который возвращает байтовый буфер с байтами, которые уже были прочитаны, но не являются спецификацией.
/// <summary>
/// UTF8 : EF BB BF
/// UTF16 BE: FE FF
/// UTF16 LE: FF FE
/// UTF32 BE: 00 00 FE FF
/// UTF32 LE: FF FE 00 00
/// </summary>
public static Encoding DetectEncoding(Stream i_Stream)
{
if (!i_Stream.CanSeek || !i_Stream.CanRead)
throw new Exception("DetectEncoding() requires a seekable and readable Stream");
// Try to read 4 bytes. If the stream is shorter, less bytes will be read.
Byte[] u8_Buf = new Byte[4];
int s32_Count = i_Stream.Read(u8_Buf, 0, 4);
if (s32_Count >= 2)
{
if (u8_Buf[0] == 0xFE && u8_Buf[1] == 0xFF)
{
i_Stream.Position = 2;
return new UnicodeEncoding(true, true);
}
if (u8_Buf[0] == 0xFF && u8_Buf[1] == 0xFE)
{
if (s32_Count >= 4 && u8_Buf[2] == 0 && u8_Buf[3] == 0)
{
i_Stream.Position = 4;
return new UTF32Encoding(false, true);
}
else
{
i_Stream.Position = 2;
return new UnicodeEncoding(false, true);
}
}
if (s32_Count >= 3 && u8_Buf[0] == 0xEF && u8_Buf[1] == 0xBB && u8_Buf[2] == 0xBF)
{
i_Stream.Position = 3;
return Encoding.UTF8;
}
if (s32_Count >= 4 && u8_Buf[0] == 0 && u8_Buf[1] == 0 && u8_Buf[2] == 0xFE && u8_Buf[3] == 0xFF)
{
i_Stream.Position = 4;
return new UTF32Encoding(true, true);
}
}
i_Stream.Position = 0;
return Encoding.Default;
}
Использование StreamReader
и направьте его, чтобы определить кодировку для вас:
using (var reader = new System.IO.StreamReader(path, true))
{
var currentEncoding = reader.CurrentEncoding;
}
И используйте идентификаторы кодовой страницы https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx, чтобы переключать логику в зависимости от нее.
Я использую Ude, который является C# портом Mozilla Universal Charset Detector. Он прост в использовании и дает действительно хорошие результаты.
Вы должны прочитать это: Как я могу определить кодировку / кодовую страницу текстового файла
Да, здесь есть один: http://en.wikipedia.org/wiki/Byte_order_mark.
Если ваш файл начинается с байтов 60, 118, 56, 46 и 49, то у вас неоднозначный случай. Это может быть UTF-8 (без спецификации) или любое из однобайтовых кодировок, таких как ASCII, ANSI, ISO-8859-1 и т. Д.
Решение для всех немцев => ÄÖÜäöüß
Эта функция открывает файл и определяет кодировку по спецификации.
Если спецификация отсутствует, файл будет интерпретирован как ANSI, но если в нем есть немецкий Umlaute в кодировке UTF8, он будет обнаружен как UTF8.
private static Encoding GetEncoding(string sFileName)
{
using (var reader = new StreamReader(sFileName, Encoding.Default, true))
{
string sContent = "";
if (reader.Peek() >= 0) // you need this!
sContent = reader.ReadToEnd();
Encoding MyEncoding = reader.CurrentEncoding;
if (MyEncoding == Encoding.Default) // Ansi detected (this happens if BOM is missing)
{ // Look, if there are typical UTF8 chars in this file...
string sUmlaute = "ÄÖÜäöüß";
bool bUTF8CharDetected = false;
for (int z=0; z<sUmlaute.Length; z++)
{
string sUTF8Letter = sUmlaute.Substring(z, 1);
string sUTF8LetterInAnsi = Encoding.Default.GetString(Encoding.UTF8.GetBytes(sUTF8Letter));
if (sContent.Contains(sUTF8LetterInAnsi))
{
bUTF8CharDetected = true;
break;
}
}
if (bUTF8CharDetected) MyEncoding = Encoding.UTF8;
}
return MyEncoding;
}
}