Как я могу определить кодировку / кодовую страницу текстового файла
В нашем приложении мы получаем текстовые файлы (.txt
, .csv
и т. д.) из разных источников. При чтении эти файлы иногда содержат мусор, поскольку файлы были созданы в другой / неизвестной кодовой странице.
Есть ли способ (автоматически) определить кодовую страницу текстового файла?
detectEncodingFromByteOrderMarks
, на StreamReader
конструктор, работает для UTF8
и другие файлы, помеченные юникодом, но я ищу способ обнаружения кодовых страниц, например ibm850
, windows1252
,
Спасибо за ваши ответы, это то, что я сделал.
Файлы, которые мы получаем от конечных пользователей, не имеют понятия о кодовых страницах. Получатели также являются конечными пользователями, и теперь они знают о кодовых страницах: кодовые страницы существуют и раздражают.
Решение:
- Откройте полученный файл в блокноте, посмотрите на искаженный фрагмент текста. Если кого-то зовут Франсуа или что-то еще, с вашим человеческим интеллектом вы можете догадаться об этом.
- Я создал небольшое приложение, с помощью которого пользователь может открыть файл и ввести текст, который, как он знает, будет отображаться в файле при использовании правильной кодовой страницы.
- Переберите все кодовые страницы и отобразите те, которые дают решение, с предоставленным пользователем текстом.
- Если появляется более одной кодовой страницы, попросите пользователя указать больше текста.
21 ответ
Вы не можете обнаружить кодовую страницу, вам нужно сказать это. Вы можете проанализировать байты и угадать это, но это может дать некоторые странные (иногда забавные) результаты. Я не могу найти его сейчас, но я уверен, что Блокнот можно обмануть для отображения английского текста на китайском языке.
В любом случае, это то, что вам нужно прочитать: Абсолютный минимум, который должен быть у каждого разработчика программного обеспечения. Абсолютно, положительно необходимо знать о Unicode и наборах символов (никаких оправданий!).
В частности, Джоэл говорит:
Единственный самый важный факт о кодировках
Если вы полностью забудете все, что я только что объяснил, пожалуйста, запомните один чрезвычайно важный факт. Не имеет смысла иметь строку, не зная, какую кодировку она использует. Вы больше не можете засовывать голову в песок и делать вид, что "простой" текст - это ASCII. Там нет такого понятия, как простой текст.
Если у вас есть строка, в памяти, в файле или в сообщении электронной почты, вы должны знать, в какой кодировке она находится, или вы не можете ее интерпретировать или правильно отобразить для пользователей.
Если вы хотите обнаружить кодировки, отличные от UTF (т. Е. Нет спецификации), вы в основном дошли до эвристики и статистического анализа текста. Возможно, вы захотите взглянуть на статью Mozilla об универсальном обнаружении кодировок ( та же ссылка, с лучшим форматированием через Wayback Machine).
Вы пробовали порт C# для Mozilla Universal Charset Detector
Пример с http://code.google.com/p/ude/
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.");
}
}
}
Вы не можете обнаружить кодовую страницу
Это явно неверно. Каждый веб-браузер имеет своего рода универсальный детектор кодировки для работы со страницами, которые не имеют никаких признаков кодировки. У Firefox есть один. Вы можете скачать код и посмотреть, как он это делает. Смотрите некоторые документы здесь. По сути, это эвристика, но она работает очень хорошо.
Учитывая разумное количество текста, можно даже определить язык.
Вот еще один, который я только что нашел с помощью Google:
Если кто-то ищет решение на 93,9%. Это работает для меня:
public static class StreamExtension
{
/// <summary>
/// Convert the content to a string.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
public static string ReadAsString(this Stream stream)
{
var startPosition = stream.Position;
try
{
// 1. Check for a BOM
// 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
return streamReader.ReadToEnd();
}
catch (DecoderFallbackException ex)
{
stream.Position = startPosition;
// 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
return streamReader.ReadToEnd();
}
}
}
Я знаю, что уже слишком поздно для этого вопроса, и это решение не понравится некоторым (из-за его англо-ориентированного уклона и отсутствия статистического / эмпирического тестирования), но оно очень хорошо для меня, особенно для обработки выгруженных данных CSV:
http://www.architectshack.com/TextFileEncodingDetector.ashx
Преимущества:
- Встроенное обнаружение спецификации
- По умолчанию / резервная кодировка настраивается
- довольно надежный (по моему опыту) для западноевропейских файлов, содержащих некоторые экзотические данные (например, французские имена) со смесью файлов в стиле UTF-8 и Latin-1- в основном это большая часть среды США и Западной Европы.
Примечание: я тот, кто написал этот класс, так что, очевидно, возьмите его с крошкой соли!:)
Notepad++ имеет эту функцию из коробки. Это также поддерживает изменение этого.
В поисках другого решения я обнаружил, что
https://code.google.com/p/ude/
это решение довольно тяжело.
Мне нужно было некоторое базовое обнаружение кодирования, основанное на 4 первых байтах и, возможно, обнаружение кодировки xml - поэтому я взял пример исходного кода из Интернета и добавил слегка измененную версию
http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html
написано для Java.
public static Encoding DetectEncoding(byte[] fileContent)
{
if (fileContent == null)
throw new ArgumentNullException();
if (fileContent.Length < 2)
return Encoding.ASCII; // Default fallback
if (fileContent[0] == 0xff
&& fileContent[1] == 0xfe
&& (fileContent.Length < 4
|| fileContent[2] != 0
|| fileContent[3] != 0
)
)
return Encoding.Unicode;
if (fileContent[0] == 0xfe
&& fileContent[1] == 0xff
)
return Encoding.BigEndianUnicode;
if (fileContent.Length < 3)
return null;
if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
return Encoding.UTF8;
if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
return Encoding.UTF7;
if (fileContent.Length < 4)
return null;
if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
return Encoding.UTF32;
if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
return Encoding.GetEncoding(12001);
String probe;
int len = fileContent.Length;
if( fileContent.Length >= 128 ) len = 128;
probe = Encoding.ASCII.GetString(fileContent, 0, len);
MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
// Add '[0].Groups[1].Value' to the end to test regex
if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
{
// Typically picks up 'UTF-8' string
Encoding enc = null;
try {
enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
}catch (Exception ) { }
if( enc != null )
return enc;
}
return Encoding.ASCII; // Default fallback
}
Достаточно прочитать первые 1024 байта из файла, но я загружаю весь файл.
Я сделал нечто подобное в Python. По сути, вам нужно много примеров данных из различных кодировок, которые разбиты скользящим двухбайтовым окном и хранятся в словаре (хэш), который основан на байтовых парах, предоставляющих значения списков кодировок.
Учитывая этот словарь (хэш), вы берете свой входной текст и:
- если он начинается с любого символа спецификации ('\xfe\xff' для UTF-16-BE, '\xff\xfe' для UTF-16-LE, '\xef\xbb\xbf' для UTF-8 и т. д.), я относиться к этому как предложено
- если нет, то возьмите достаточно большой образец текста, возьмите все пары байтов этого образца и выберите кодировку, которая является наименее распространенной из словаря.
Если вы также сэмплировали тексты в кодировке UTF, которые не начинаются с какой-либо спецификации, второй шаг будет охватывать те, которые были пропущены с первого шага.
Пока что это работает для меня (примерные данные и последующие входные данные являются субтитрами на разных языках) с уменьшением количества ошибок.
Инструмент "uchardet" делает это хорошо, используя модели распределения частот символов для каждой кодировки. Большие файлы и более "типичные" файлы имеют большую уверенность (очевидно).
На Ubuntu, вы просто apt-get install uchardet
,
В других системах получите источник, использование и документы здесь: https://github.com/BYVoid/uchardet
Конструктор класса StreamReader принимает параметр "обнаружение кодировки".
Откройте файл в AkelPad(или просто скопируйте / вставьте искаженный текст), перейдите в "Правка" -> "Выделение" -> "Перекодировать" -> установите флажок "Автоопределение".
Получил ту же проблему, но пока не нашел хорошего решения для ее автоматического определения. Теперь я использую PsPad (www.pspad.com) для этого;) Работает нормально
Если вы можете связаться с библиотекой C, вы можете использовать libenca
, Смотрите http://cihar.com/software/enca/. Со страницы руководства:
Enca читает заданные текстовые файлы или стандартный ввод, если они не заданы, и использует знания об их языке (должны быть поддержаны вами), а также смесь синтаксического анализа, статистического анализа, угадывания и черной магии для определения их кодировок.
Это GPL v2.
Я на самом деле искал общий, не программирующий способ определения кодировки файла, но пока не нашел. Что я нашел, протестировав с разными кодировками, так это то, что мой текст был UTF-7
Итак, где я впервые делал: StreamReader file = File.OpenText(fullfilename);
Мне пришлось изменить его на: StreamReader file = new StreamReader(полное имя файла, System.Text.Encoding.UTF7);
OpenText предполагает, что это UTF-8.
Вы также можете создать StreamReader, как этот новый StreamReader(fullfilename, true), второй параметр означает, что он должен попытаться обнаружить кодировку по метке byteorder файла, но в моем случае это не сработало.
Как дополнение к сообщению ITmeze, я использовал эту функцию для преобразования вывода порта C# для Mozilla Universal Charset Detector
private Encoding GetEncodingFromString(string codePageName)
{
try
{
return Encoding.GetEncoding(codePageName);
}
catch
{
return Encoding.ASCII;
}
}
Спасибо @ Erik Aronesty за упоминание uchardet
,
Тем временем (тот же?) Инструмент существует для Linux: chardet
,
Или на Cygwin вы можете использовать: chardetect
,
См. Справочную страницу chardet: https://www.commandlinux.com/man-page/man1/chardetect.1.html
Это будет эвристически определять (угадывать) кодировку символов для каждого данного файла и сообщать имя и уровень достоверности для обнаруженной кодировки символов каждого файла.
попробуйте установить Perl-модуль Text::Unaccent::PurePerl, набрав cpanm Text::Unaccent. Это создаст файл build.log, который в некоторых приложениях отображается как китайский, а в других — как английский. cpanm — исходный текст. достаточно иметь пробелы в языке, чтобы сравнить частоту распространения слов с помощью статистического теста
Поскольку это в основном сводится к эвристике, это может помочь использовать кодирование ранее полученных файлов из того же источника в качестве первой подсказки.
Большинство людей (или приложений) делают вещи в одном и том же порядке каждый раз, часто на одной и той же машине, поэтому вполне вероятно, что когда Боб создает файл.csv и отправляет его Мэри, он всегда будет использовать Windows-1252 или какой бы ни была его машина по умолчанию.
Там, где это возможно, немного обучения клиентов тоже не повредит:-)
Я использую этот код, чтобы обнаружить кодировку ANSI по умолчанию в Юникоде и Windows по умолчанию при чтении файла. Для других кодировок необходима проверка содержимого, вручную или путем программирования. Это можно использовать для сохранения текста с той же кодировкой, что и при его открытии. (Я использую VB.NET)
'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default)
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()
10Y (!) Прошло с тех пор, как об этом спросили, и до сих пор я не вижу упоминания о хорошем решении MS, не подпадающем под GPL: IMultiLanguage2 API.
Большинство уже упомянутых библиотек основаны на UDE Mozilla - и кажется разумным, что браузеры уже справились с подобными проблемами. Я не знаю, каково решение Chrome, но с тех пор, как IE 5.0 MS выпустила их, это:
- Без проблем с лицензией GPL и тому подобным,
- Поддерживается и поддерживается, вероятно, навсегда,
- Дает богатый вывод - все действительные кандидаты на кодирование / кодовые страницы наряду с оценками достоверности,
- Удивительно прост в использовании (это единственный вызов функции).
Это нативный COM-вызов, но вот очень хорошая работа Карстена Цоймера, которая обрабатывает беспорядок взаимодействия для использования.net. Вокруг есть и другие, но в целом эта библиотека не получает того внимания, которого заслуживает.