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

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


person Pablo Retyk    schedule 26.05.2009    source источник
comment
Под двоичным вы подразумеваете исполняемый файл или просто случайные вещи?   -  person Vizu    schedule 26.05.2009
comment
просто все, что не является текстом, например изображение, музыка, msword, исполняемый файл, dll и т. д.   -  person Pablo Retyk    schedule 26.05.2009
comment
Если вы скажете мне, как человек может определить разницу .. Я могу помочь вам с частью C # :)   -  person G.Y    schedule 11.08.2015


Ответы (11)


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

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

person Ron Warholic    schedule 26.05.2009
comment
Спасибо, я искал 4 последовательных двоичных файла с нулевым значением \ 0 \ 0 \ 0 \ 0, похоже, их много, поэтому я протестировал его в 50 случайных файлах, и он работает. - person Pablo Retyk; 26.05.2009
comment
Четыре последовательных нуля не могли сказать, что некоторые файлы .png были двоичными, поэтому я попробовал два последовательных нуля, и это сработало лучше. - person Adam Bruss; 29.10.2012
comment
Если текстовый файл имеет формат ASCII или UTF-8, нахождения одного нулевого байта должно быть достаточно, чтобы сделать вывод, что это не так. Это не сработает для файлов UTF-16 и UTF-32, но то же самое с большинством текстовых редакторов ;-) - person John Dvorak; 15.03.2013

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

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

person zvolkov    schedule 26.05.2009
comment
Спасибо, я сделал что-то подобное, искал последовательное количество нулей. - person Pablo Retyk; 26.05.2009

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

Задний план

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

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

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

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

  1. Проверьте наличие множества управляющих символов, например, ищите несколько последовательных нулевых символов.
  2. Проверьте UTF, Unicode, кодировки, спецификацию, порядок байтов и аналогичные аспекты.

Моя цель:

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

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

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

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

person bhavik shah    schedule 30.10.2014
comment
Функция getSize отсутствует. Спасибо за код. Важные части были использованы, и тестирование пока идет хорошо. - person Atron Seige; 17.09.2015
comment
Мне действительно нравится, что это решение не читает весь файл. Это значительно упрощает запуск инструмента, который наблюдает за всем каталогом, который может содержать видео размером 50 МБ. - person Katana314; 04.12.2015
comment
@AtronSeige, вы можете использовать new FileInfo(path).Length, чтобы получить размер файла. - person Jeremy Cook; 21.11.2016
comment
Это тоже помогает согласовать кодировку. Я пишу инструмент для согласования кодировки в marketplace.visualstudio.com/, которые используют ваше решение. - person lindexi; 19.01.2017
comment
Спасибо. Сработало кроме одного случая. Я взял XML-файл, открыл его в Блокноте и сохранил как Unicode (также добавил несколько посторонних символов). Я сохраняю файл в блоге или текстовом поле столбца данных MySQL, а затем записываю его обратно на диск. - person NealWalters; 14.04.2017
comment
спасибо, у меня сработала проблема, с которой я столкнулся. Файлы сохраняются на сетевой диск и иногда заполняются всеми нулевыми символами. - person Erin; 03.08.2017

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

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

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

person Александр Киричек    schedule 20.05.2015
comment
Я бы включил FF и VT. (Я догадываюсь, что HT уже есть ..) - person TaW; 26.04.2017
comment
Вероятно, вам также следует исключить '\ t' - person Cocowalla; 27.04.2017

Если реальный вопрос здесь: «Можно ли читать и писать этот файл с помощью 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'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;
    }
}
person DigitalMindspring    schedule 07.07.2011
comment
Не работает для простого текстового файла, где я поставил à (французский a с акцентом). - person Alexis Pautrot; 26.09.2012

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

Это должно стать довольно хорошим решением для того, что вам нужно.

person Steven de Salas    schedule 31.12.2014

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

person Jeff Yates    schedule 26.05.2009
comment
Иногда ребята вроде меня могут изменить расширение двоичного файла на .txt. - person Kirtan; 26.05.2009
comment
Очевидно, но он просил дешево и грязно - нет надежного способа, кроме как попросить человека прочитать это. - person Jeff Yates; 26.05.2009
comment
это хорошо, к сожалению, я не имею дело с общими расширениями, я пишу какой-то список всех файлов, и мне нужно распределить их по категориям: bin или text, большинство людей делают это вручную, но поскольку я ленив, я предпочитаю писать код. - person Pablo Retyk; 26.05.2009
comment
Многие люди экспортируют файлы Excel с расширением .xls, которые на самом деле являются файлами csv или html. - person Tim Schmelter; 15.11.2013
comment
@TimSchmelter: Я сказал быстро и грязно, не надежно и на 100% эффективно. :) - person Jeff Yates; 15.11.2013
comment
@JeffYates: Я знаю, но большинство людей (например, Киртан) думают, что подход расширения - это просто проблема, если кто-то пытается загрузить exe как txt или около того. Многие файлы не такие, какими должны быть даже при нормальных обстоятельствах. - person Tim Schmelter; 15.11.2013

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

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

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

person Chad Ruppert    schedule 26.05.2009
comment
О, \ me не уверен, что регулярное выражение для файла размером в несколько мегабайт почти «быстро». - person Anton Kraievyi; 16.02.2011
comment
зависит от определения слова "быстро". быстро бегать или быстро писать? :) - person Chad Ruppert; 12.09.2011

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
person foson    schedule 26.05.2009
comment
Это работает, если файл гарантированно имеет формат UTF8 / 16 или двоичный. Но что, если это не так? Что, если это текстовый файл, не закодированный ни в ASCII, ни в UTF-8/16. Что, если он закодирован в кодовой странице Big5? Или ISO-8859-1? У них нет спецификации. Итак ... как также прикрыть это дело? - person Cheeso; 26.05.2009
comment
Если файл имеет формат (US-) ASCII, это на самом деле UTF-8, потому что символы с 7-битным символьным кодом переводятся на себя в UTF-8, но если это сделано на какой-то локализованной кодовой странице ANSI, он все равно будет распознаваться как двоичный с помощью вышеуказанного метода. - person mg30rg; 08.08.2013

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

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

person shytikov    schedule 05.01.2012
comment
Это, конечно, зависит от используемой кодировки. - person ; 06.03.2012
comment
А преобразование файла произвольной длины в массив байтов и преобразование в строку может легко потребовать чрезмерного количества ресурсов. Просто подумайте о файле журнала размером 2 ГБ (который определенно является текстовым файлом) ... если вы хотите сравнить его с его неконвертированной версией, вам нужно зарезервировать более 4 ГБ памяти, чем сравнивать его постранично .. Это даже не быстро. - person mg30rg; 08.08.2013

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

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

person Tyler Long    schedule 25.03.2015