Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

Фон

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

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

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

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

  • Проверьте наличие множества управляющих символов, например, для поиска нескольких последовательных нулевых символов.
  • Проверьте UTF, Юникод, Кодировки, Спецификация, Байт-ордеры и другие аналогичные аспекты.

Моя цель:

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

Представленное решение работает для меня на тестовых данных, которые включают в себя 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 как контрольные символы между прочим

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

Решение

static void testBinaryFile(string folderPath)
{
    List<string> output = new List<string>();
    foreach (string filePath in getFiles(folderPath, true))
    {
        output.Add(isBinary(filePath).ToString() + "  ----  " + filePath);
    }
    Clipboard.SetText(string.Join("\n", output), TextDataFormat.Text);
}

public static List<string> getFiles(string path, bool recursive = false)
{
    return Directory.Exists(path) ?
        Directory.GetFiles(path, "*.*",
        recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).ToList() :
        new List<string>();
}    

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
}

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

Другие интересные и родственные ссылки:

Ответ 4

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

/// <summary>
/// Detect if a file is text and detect the encoding.
/// </summary>
/// <param name="encoding">
/// The detected encoding.
/// </param>
/// <param name="fileName">
/// The file name.
/// </param>
/// <param name="windowSize">
/// The number of characters to use for testing.
/// </param>
/// <returns>
/// true if the file is text.
/// </returns>
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 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;
    }
}

Ответ 5

В то время как это не является надежным, это должно проверить, имеет ли он какой-либо двоичный контент.

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

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

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

Я только что проверил для последовательных нулей (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 из них для последовательных нулей (лично я бы вывел его двоичный файл, если 2 или 3 из них соответствуют - нули в текстовых файлах редки).

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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