如何区分“二进制”和“文本”文件?

时间:2009-02-19 23:46:35

标签: unix language-agnostic ascii binaryfiles file-format

非正式地,我们大多数人都知道存在'二进制'文件(目标文件,图像,电影,可执行文件,专有文档格式等)和'文本'文件(源代码,XML文件,HTML文件,电子邮件等) )。

通常,您需要知道文件的内容才能对其执行任何有用的操作,并且如果编码是“二进制”或“文本”,则形成该视点,这并不重要。当然文件只存储数据字节,因此它们都是“二进制”,而“文本”并不意味着什么,而不知道编码。然而,谈论'二进制'和'文本'文件仍然很有用,但为了避免冒犯任何具有这种不精确定义的人,我将继续使用'恐慌'报价。

但是,有各种工具可以处理各种文件,实际上,您希望根据文件是“文本”还是“二进制”来执行不同的操作。这方面的一个例子是在控制台上输出数据的任何工具。简单的“文本”看起来很好,很有用。 '二进制'数据会扰乱您的终端,并且通常无法查看。 GNU grep在确定它是否应该输出与控制台的匹配时至少使用这种区别。

所以,问题是,如何判断文件是“文本”还是“二进制”?而且要进一步限制,你如何在类似Linux的文件系统上讲述?我不知道任何文件系统元数据指示文件的“类型”,因此通过检查文件的内容,我如何判断它是“文本”还是“二进制”?为简单起见,我们将“text”限制为可在用户控制台上打印的字符。特别是你将如何实现这个? (我认为这是暗示在这个网站上,但我想一般来说,指向现有的代码,这应该是有用的,我应该指定),我不是真的在我可以使用的现有程序之后做什么此

11 个答案:

答案 0 :(得分:62)

您可以使用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

答案 1 :(得分:13)

您可以使用

确定文件的MIME type
file --mime FILENAME

Linux上的简写为file -i,macOS上为file -I(大写i)(见注释)。

如果以text/开头,则为文本,否则为二进制。唯一的例外是XML应用程序。您可以通过在文件类型末尾查找+xml来匹配这些内容。

答案 2 :(得分:13)

我公司制作的电子表格软件可以读取多种二进制文件格式以及文本文件。

我们首先看一下我们认识到的magic number的前几个字节。如果我们无法识别我们读取的任何二进制类型的幻数,那么我们会查看文件的前2K字节,看它是否为UTF-8UTF-16或在主机操作系统的当前code page中编码的文本文件。如果它没有通过这些测试,我们假设它不是我们可以处理的文件并抛出适当的异常。

答案 3 :(得分:4)

好吧,如果您只是检查整个文件,请查看每个字符是否都可以使用isprint(c)进行打印。它对Unicode来说有点复杂。

区分unicode文本文件MSDN offers some great advice as to what to do

它的要点是首先检查前四个字节:

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的区域设置变体(如果您的平台上已有)。

答案 4 :(得分:3)

Perl有一个不错的启发式方法。使用-B运算符测试二进制(及其相反的-T以测试文本)。这是一个单行列表文本文件:

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

(请注意,那些没有前一美元的下划线是正确的(RTFM)。)

答案 5 :(得分:2)

大多数尝试分辨差异的程序都使用启发式方法,例如检查文件的第一个 n 字节,并查看这些字节 all 是否符合'text'的条件是否(即,它们都属于可打印的ASCII字符范围内)。对于更精细的干扰,在类UNIX系统上总是有'file'命令。

答案 6 :(得分:2)

这是一个古老的话题,但也许有人会觉得这很有用。 如果您必须在脚本中决定某个文件是什么,那么您可以这样做:

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

这将获取文件类型,使用静默grep,您可以决定它是否为文本。

答案 7 :(得分:2)

列出当前目录/子目录中的文本文件名:

$ grep -rIl ''

二进制文件:

$ grep -rIL ''

要检查特定文件,请稍微修改命令:

$ grep -qI '' FILE
然后,退出状态' 0'意味着文件是文本; ' 1' - 二进制。 可以检查:

$ echo $?

答案 8 :(得分:1)

一个简单的检查是它是否有\0个字符。文本文件没有它们。

答案 9 :(得分:1)

如前所述* 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 */
    }

答案 10 :(得分:1)

您可以使用libmagic这是Unix file命令行的库版本。

有许多语言的包装器: