Как определить, является ли файл двоичным или текстовым в c #?

Мне нужно определить в 80%, если файл является двоичным или текстовым, есть ли способ сделать это даже быстро и грязно / уродливо в c #?

Я бы, вероятно, искал множество управляющих символов, которые обычно присутствовали бы в двоичном файле, но редко в текстовом файле. Двоичные файлы, как правило, используют 0 достаточно, чтобы просто тестирование для многих 0 байтов, вероятно, было бы достаточным для того, чтобы поймать большинство файлов. Если вам небезразлична локализация, вам необходимо также протестировать многобайтовые шаблоны.

Как было сказано, вы всегда можете оказаться неудачным и получить двоичный файл, который выглядит как текст или наоборот.

Есть метод, называемый цепями Маркова. Сканируйте несколько файлов моделей обоих типов и для каждого байтового значения от 0 до 255 собирайте статистику (в основном вероятность) следующего значения. Это даст вам профиль размером 64 КБ (256х256), который можно сравнить с файлами во время выполнения (в пределах порога%).

Предположительно, так работает функция автообнаружения кодировщика браузеров.

Разделяйте мое решение в надежде, что оно помогает другим, поскольку оно помогает мне с этих сообщений и форумов.

Задний план

Я изучал и изучал решение для этого же. Однако я ожидал, что это будет просто или слегка искажено.

Тем не менее, большинство попыток предоставляют запутанные решения здесь, а также другие источники и погружения в Unicode, UTF-серии , спецификации, кодировки, байтовые заказы. В ходе этого процесса я также отправился в бездорожье, а также в таблицы Ascii и кодовые страницы .

В любом случае, я придумал решение, основанное на идее проверки считывателя streamа и пользовательских контрольных символов .

Он построен с учетом различных советов и советов, представленных на форуме и в других местах, таких как:

  1. Проверьте количество управляющих символов, например, ищите несколько последовательных нулевых символов.
  2. Проверьте наличие UTF, Unicode, Encodings, BOM, Byte Orders и аналогичные аспекты.

Моя цель:

  1. Он не должен полагаться на байтовые заказы, кодировки и другие более сложные эзотерические работы.
  2. Это должно быть относительно легко реализовать и легко понять.
  3. Он должен работать на всех типах файлов.

Представленное решение работает для меня на тестовых данных, которые include в себя mp3, eml, txt, info, flv, mp4, pdf, gif, png, jpg. Это дает ожидаемые результаты.

Как работает решение

Я полагаюсь на конструктор по умолчанию StreamReader, чтобы сделать то, что он может сделать лучше всего в отношении определения характеристик кодирования файлов, которые по умолчанию используют UTF8Encoding .

Я создал свою собственную версию проверки для настраиваемого состояния char, потому что Char.IsControl не кажется полезным. В нем говорится:

Управляющими символами являются форматирование и другие непечатаемые символы, такие как ACK, BEL, CR, FF, LF и VT. Стандарт Unicode присваивает коды от \ U0000 до \ U001F, \ U007F и от \ U0080 до \ U009F для управления символами. Эти значения должны интерпретироваться как управляющие символы, если их использование в другом случае не определяется приложением. Он рассматривает LF и CR как контрольные символы среди прочего

Это делает его непригодным, так как текстовые файлы include CR и LF как минимум.

Решение

static void testBinaryFile(string folderPath) { List output = new List(); foreach (string filePath in getFiles(folderPath, true)) { output.Add(isBinary(filePath).ToString() + " ---- " + filePath); } Clipboard.SetText(string.Join("\n", output), TextDataFormat.Text); } public static List getFiles(string path, bool recursive = false) { return Directory.Exists(path) ? Directory.GetFiles(path, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).ToList() : new List(); } public static bool isBinary(string path) { long length = getSize(path); if (length == 0) return false; using (StreamReader stream = new StreamReader(path)) { int ch; while ((ch = stream.Read()) != -1) { if (isControlChar(ch)) { return true; } } } return false; } public static bool isControlChar(int ch) { return (ch > Chars.NUL && ch < Chars.BS) || (ch > Chars.CR && ch < Chars.SUB); } public static class Chars { public static char NUL = (char)0; // Null char public static char BS = (char)8; // Back Space public static char CR = (char)13; // Carriage Return public static char SUB = (char)26; // Substitute } 

Если вы попробуете решение выше, сообщите мне, что оно работает для вас или нет.

Другие интересные и связанные ссылки:

  • О UTF и спецификации на Unicode.org
  • Файлы примеров Unicode
  • Как определить кодировку текстового файла и
  • Обнаружение кодировки файлов в Csharp

Если реальный вопрос здесь: «Можно ли прочитать и записать этот файл с помощью StreamReader / StreamWriter без изменений?», То ответ здесь:

 ///  /// Detect if a file is text and detect the encoding. ///  ///  /// The detected encoding. ///  ///  /// The file name. ///  ///  /// The number of characters to use for testing. ///  ///  /// true if the file is text. ///  public static bool IsText(out Encoding encoding, string fileName, int windowSize) { using (var fileStream = File.OpenRead(fileName)) { var rawData = new byte[windowSize]; var text = new char[windowSize]; var isText = true; // Read raw bytes var rawLength = fileStream.Read(rawData, 0, rawData.Length); fileStream.Seek(0, SeekOrigin.Begin); // Detect encoding correctly (from Rick Strahl's blog) // http://www.west-wind.com/weblog/posts/2007/Nov/28/Detecting-Text-Encoding-for-StreamReader if (rawData[0] == 0xef && rawData[1] == 0xbb && rawData[2] == 0xbf) { encoding = Encoding.UTF8; } else if (rawData[0] == 0xfe && rawData[1] == 0xff) { encoding = Encoding.Unicode; } else if (rawData[0] == 0 && rawData[1] == 0 && rawData[2] == 0xfe && rawData[3] == 0xff) { encoding = Encoding.UTF32; } else if (rawData[0] == 0x2b && rawData[1] == 0x2f && rawData[2] == 0x76) { encoding = Encoding.UTF7; } else { encoding = Encoding.Default; } // Read text and detect the encoding using (var streamReader = new StreamReader(fileStream)) { streamReader.Read(text, 0, text.Length); } using (var memoryStream = new MemoryStream()) { using (var streamWriter = new StreamWriter(memoryStream, encoding)) { // Write the text to a buffer streamWriter.Write(text); streamWriter.Flush(); // Get the buffer from the memory stream for comparision var memoryBuffer = memoryStream.GetBuffer(); // Compare only bytes read for (var i = 0; i < rawLength && isText; i++) { isText = rawData[i] == memoryBuffer[i]; } } } return isText; } } 

Хотя это не является надежным, это должно проверить, есть ли у него двоичный контент.

 public bool HasBinaryContent(string content) { return content.Any(ch => char.IsControl(ch) && ch != '\r' && ch != '\n'); } 

Потому что, если существует какой-либо управляющий символ (кроме стандартного \r\n ), то это, вероятно, не текстовый файл.

Быстро и грязно использовать расширение файла и искать обычные текстовые расширения, такие как .txt. Для этого вы можете использовать вызов Path.GetExtension . Все остальное на самом деле не было бы classифицировано как «быстрое», хотя это может быть грязно.

Действительно действительно грязным способом было бы создать регулярное выражение, которое принимает только стандартный текст, знаки пунктуации, символы и символы пробелов, загружает часть файла в текстовый stream, а затем запускает его против регулярного выражения. В зависимости от того, что квалифицируется как чистый текстовый файл в проблемном домене, успешные совпадения не указывают на двоичный файл.

Чтобы учесть unicode, обязательно отметьте кодировку в своем streamе как таковой.

Это действительно субоптимально, но вы сказали быстро и грязно.

Отличный вопрос! Я был удивлен, что .NET не обеспечивает легкого решения для этого.

Следующий код работал для меня, чтобы различать изображения (png, jpg и т. Д.) И текстовые файлы.

Я только что проверил для последовательных нhive ( 0x00 ) в первых 512 байт, в соответствии с предложениями Рона Уорхолика и Адама Брисса:

 if (File.Exists(path)) { // Is it binary? Check for consecutive nulls.. byte[] content = File.ReadAllBytes(path); for (int i = 1; i < 512 && i < content.Length; i++) { if (content[i] == 0x00 && content[i-1] == 0x00) { return Convert.ToBase64String(content); } } // No? return text return File.ReadAllText(path); } 

Очевидно, что это быстрый и грязный подход, однако его можно легко расширить, разбив файл на 10 кусков по 512 байт и проверив 8 из них для последовательных нhive (лично я бы вывел его двоичный файл, если 2 или 3 из них соответствуют - нули в текстовых файлах редки).

Это должно обеспечить довольно хорошее решение для вас.

http://codesnipers.com/?q=node/68 описывает, как обнаружить UTF-16 по сравнению с UTF-8, используя знак байтового заказа (который может отображаться в вашем файле). Он также предлагает прокрутить несколько байтов, чтобы убедиться, что они соответствуют шаблону последовательности нескольких байтов UTF-8 (см. Ниже), чтобы определить, является ли ваш файл текстовым файлом.

  • 0xxxxxxx ASCII <0x80 (128)
  • 110xxxxx 10xxxxxx 2-байтовый> = 0x80
  • 1110xxxx 10xxxxxx 10xxxxxx 3-байтовый> = 0x400
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4-байтовый> = 0x10000

Как насчет другого способа: определить длину двоичного массива, представить содержимое файла и сравнить его с длиной строки, которую вы будете иметь после преобразования данного двоичного массива в текст.

Если длина такая же, в файле нет «нечитаемых» символов, это текст (я уверен, на 80%).

Другой способ – обнаружить кодировку файла с помощью UDE . Если charset обнаружен успешно, вы можете быть уверены, что это текст, иначе он двоичный. Потому что двоичный код не имеет кодировки.

Конечно, вы можете использовать другую библиотеку обнаружения кодировки, отличную от UDE. Если библиотека обнаружения кодировки достаточно хороша, этот подход может обеспечить 100% правильность.

Давайте будем гением компьютера.