Как отличить двоичные файлы от текстовых?

Неформально большинство из нас понимают, что существуют «двоичные» файлы (объектные файлы, изображения, фильмы, исполняемые файлы, проприетарные форматы документов и т. Д.) И «текстовые» файлы (исходный код, файлы XML, файлы HTML, электронная почта и т. Д.).

В общем, вам нужно знать содержимое файла, чтобы иметь возможность делать с ним что-нибудь полезное, и формировать эту точку зрения, если кодировка является «двоичной» или «текстовой», это не имеет особого значения. И, конечно же, файлы просто хранят байты данных, поэтому все они являются «двоичными», а «текст» ничего не значит без знания кодировки. Тем не менее, по-прежнему полезно говорить о «двоичных» и «текстовых» файлах, но, чтобы никого не обидеть этим неточным определением, я буду продолжать использовать «пугающие» кавычки.

Однако существуют различные инструменты, которые работают с широким спектром файлов, и с практической точки зрения вы хотите сделать что-то другое в зависимости от того, является ли файл «текстовым» или «двоичным». Примером этого является любой инструмент, выводящий данные на консоль. Обычный «текст» будет хорошо смотреться и будет полезен. «двоичные» данные портят ваш терминал и, как правило, бесполезны для просмотра. GNU grep по крайней мере использует это различие при определении, следует ли выводить совпадения на консоль.

Итак, вопрос в том, как определить, является ли файл «текстовым» или «двоичным»? А чтобы ограничить, то как вы это скажете на файловой системе, подобной Linux? Мне не известны какие-либо метаданные файловой системы, которые указывают на «тип» файла, поэтому возникает вопрос, проверяя содержимое файла, как я могу определить, является ли он «текстовым» или «двоичным»? А для простоты ограничим «текст» символами, которые можно распечатать на консоли пользователя. И, в частности, как бы вы реализовали это? (Я думал, что это подразумевается на этом сайте, но я думаю, что в целом полезно указать на существующий код, который делает это, я должен был указать), я не совсем понимаю, какие существующие программы я могу использовать для выполнения это.


person benno    schedule 19.02.2009    source источник


Ответы (11)


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

Сначала мы смотрим на первые несколько байтов узнаваемого нами магического числа. Если мы не распознаем магическое число какого-либо из двоичных типов, которые мы читаем, мы смотрим вплоть до первых 2 Кбайт файла, чтобы увидеть, выглядит ли он UTF-8, UTF-16 или текстовый файл, закодированный на текущей кодовой странице хоста, работающего система. Если он не проходит ни один из этих тестов, мы предполагаем, что это не тот файл, с которым мы можем иметь дело, и генерируем соответствующее исключение.

person Joe Erickson    schedule 20.02.2009

Вы можете использовать команду file. Он выполняет ряд тестов с файлом (man file), чтобы определить, является ли он двоичным или текстовым. Вы можете посмотреть / позаимствовать его исходный код, если вам нужно это сделать из C.

file README
README: ASCII English text, with very long lines

file /bin/bash
/bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped
person naumcho    schedule 19.02.2009
comment
+1 Если это система Linux, файл будет иметь гораздо лучшую эвристику, чем все, что вы создадите сами. - person Adam Lassek; 21.02.2009
comment
Да, если файл доступен, это будет лучший инструмент для работы. Нет вопросов! Также "файл -I" - это изящный трюк. Я не думал об устранении моей конкретной проблемы, однако не думаю, что смогу справиться с накладными расходами на производительность. Спасибо! - person benno; 21.02.2009

Вы можете определить MIME-тип файла с помощью

file --mime FILENAME

Сокращение - file -i в Linux и file -I (заглавная i) в macOS (см. Комментарии).

Если он начинается с text/, это текст, в противном случае - двоичный. Единственное исключение - приложения XML. Вы можете сопоставить их, ища +xml в конце типа файла.

person phihag    schedule 19.02.2009
comment
Думаю, это должен быть файл -I (верхний регистр). По крайней мере, согласно моим тестам и странице руководства. - person benno; 21.02.2009
comment
Только что посмотрел, нижний регистр правильный в Debian и gentoo Linux. Их файл - ftp.astron.com/pub/file/file-5.00. .tar.gz (или другую версию). -I (верхний) не подходит ни для одного из них. - person phihag; 21.02.2009
comment
Ха, странно. Версия для OS X (4.17) использует -I (верхняя), а версия для моих Linux-серверов (4.24) использует -i (нижняя). Как странно! Интересно, это OS X-ism, или авторы просто изменили интерфейс в промежутках между релизами. - person benno; 21.02.2009
comment
file --mime кажется совместимым как для Linux, так и для macOS. В спецификации POSIX для file -i как другой вариант, поэтому macOS использует -I, чтобы оставаться совместимым с POSIX. - person anishpatel; 09.10.2017
comment
В IIS файлы javascript обслуживаются как: application / javascript, поэтому все не так просто! - person Poul Bak; 12.05.2020

Что ж, если вы просто просматриваете весь файл, посмотрите, можно ли распечатать каждый символ с помощью isprint(c). С Unicode все становится немного сложнее.

Чтобы отличить текстовый файл в Юникоде, MSDN предлагает отличный совет относительно что делать.

Суть в том, чтобы сначала проверить до первых четырех байтов:

EF BB BF     UTF-8 
FF FE        UTF-16, little endian 
FE FF        UTF-16, big endian 
FF FE 00 00  UTF-32, little endian 
00 00 FE FF  UTF-32, big-endian 

Это расскажет вам кодировку. Затем вы захотите использовать iswprint(c) для остальных символов в текстовом файле. Для UTF-8 и UTF-16 вам необходимо проанализировать данные вручную, поскольку один символ может быть представлен переменным количеством байтов. Кроме того, если вы действительно анальный, вы захотите использовать вариант локали iswprint, если он доступен на вашей платформе.

person MSN    schedule 19.02.2009
comment
Что ж, если он не соответствует этим правилам, тогда это действительно не текстовый файл. За исключением mbcs, но это совсем другая история. - person MSN; 20.02.2009
comment
Добавление спецификации к файлам UTF-8 не поддерживается стандартом Unicode, и жаль, что они не запрещают это прямо. Кроме того, в других форматах он не обязательно присутствует. - person Deduplicator; 14.05.2014
comment
-1, потому что это зависит от текстового файла, закодированного в кодировке Unicode и имеющего метку порядка байтов. На практике текстовые файлы UTF-8 обычно не используются, а UTF-8 является наиболее распространенной кодировкой Unicode. Ответ должен хотя бы объяснить это ограничение. - person Daniel Cassidy; 16.04.2017

Чтобы перечислить имена текстовых файлов в текущем каталоге / подкаталогах:

$ grep -rIl ''

Двоичные файлы:

$ grep -rIL ''

Чтобы проверить конкретный файл, немного измените команду:

$ grep -qI '' FILE

тогда статус выхода «0» будет означать, что файл представляет собой текст; '1' - двоичный. Мог проверить:

$ echo $?

person bam    schedule 24.04.2017
comment
Это рабочее решение. Пожалуйста, объясните причину отрицательного голоса, может мне стоит как-то улучшить ответ - person bam; 24.04.2017
comment
Я тестировал его на файлах, созданных dd и nano. Ваш метод отлично работает. Мне также интересно, почему были голоса против. - person Daniel; 19.05.2017
comment
Спасибо за отличный ответ. Он заслуживает положительных отзывов. В сочетании с if..then условными выражениями, for циклом и / или find он может автоматизировать работу и становится очень мощным. - person GNUSupporter 8964民主女神 地下教會; 04.09.2018

Perl имеет неплохую эвристику. Используйте оператор -B для проверки двоичного кода (и его противоположность -T для проверки текста). Вот оболочка - однострочный список текстовых файлов:

$ find . -type f -print0 | perl -0nE 'say if -f and -s _ and -T _'

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

person bobbogo    schedule 30.01.2013

Это старая тема, но, возможно, кому-то это пригодится. Если вам нужно решить в сценарии, является ли что-то файлом, вы можете просто сделать вот так:

if file -i $1 | grep -q text;
then 
.
.
fi

Это получит тип файла, и с помощью "тихой" команды grep вы сможете решить, является ли это текстом.

person VDave    schedule 23.05.2015
comment
osx имеет два варианта для этого: нижний регистр -i будет печатать тип без классификации (например, файл, каталог); верхний регистр - я напечатаю классификацию, аналогично тому, что вы ожидаете от системы Linux. Вы захотите использовать верхний регистр -I, чтобы это работало на этой платформе - person verboze; 18.12.2015

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

Есть обертки для многих языков:

person Benoit Blanchon    schedule 23.06.2016

Большинство программ, которые пытаются определить разницу, используют эвристику, например, исследуют первые n байтов файла и проверяют, подходят ли эти байты все как «текст» или нет ( т.е. все ли они попадают в диапазон печатаемых символов ASCII). Для лучшего понимания в UNIX-подобных системах всегда есть команда file.

person dwc    schedule 19.02.2009

Одна простая проверка - есть ли в нем \0 символов. В текстовых файлах их нет.

person Georg Schölly    schedule 20.02.2009
comment
если это не utf-16 или utf32. то есть много. - person Breton; 10.08.2011

Как указывалось ранее, операционные системы * nix имеют эту возможность в команде file. Эта команда использует файл конфигурации, который определяет магические числа, содержащиеся во многих популярных файловых структурах.

Этот файл, называемый magic, исторически хранился в / etc, хотя в некоторых дистрибутивах он может находиться в / usr / share. Волшебный файл определяет смещения значений, которые, как известно, существуют в файле, и затем может исследовать эти местоположения, чтобы определить тип файла.

Структуру и описание волшебного файла можно найти, обратившись к соответствующей странице руководства (man magic)

Что касается реализации, ее можно найти в file.c сам, однако соответствующая часть команды файла, которая определяет, является ли это читаемый текст или нет, следующая

/* Make sure we are dealing with ascii text before looking for tokens */
    for (i = 0; i < nbytes - 1; i++) {
        if (!isascii(buf[i]) ||
            (iscntrl(buf[i]) && !isspace(buf[i]) &&
             buf[i] != '\b' && buf[i] != '\032' && buf[i] != '\033'
            )
           )
            return 0;   /* not all ASCII */
    }
person Steve Weet    schedule 20.02.2009