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

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

一般来说,你需要知道一个文件的内容,以便能够对它做任何有用的事情,并且如果编码是“二进制”或“文本”就形成了这个观点,这并不重要。 当然文件只是存储数据的字节,所以它们都是“二进制的”,“文本”并不意味着什么都不知道编码。 然而,讨论“二进制”和“文本”文件仍然是有用的,但为了避免这个不准确的定义,我将继续使用“恐吓”引号。

但是,有各种各样的工具可以处理各种文件,实际上,您希望根据文件是“文本”还是“二进制”来做不同的事情。 一个例子是在控制台上输出数据的任何工具。 简单的“文本”将看起来很好,是有用的。 '二进制'的数据混乱了你的terminal,一般没有用处。 当确定是否应该输出匹配到控制台时,GNU grep至less使用这个区别。

所以,问题是,你如何判断文件是“文本”还是“二进制”? 而进一步限制,你如何告诉一个像Linux文件系统的Linux? 我不知道任何文件系统的元数据表明文件的“types”,所以通过检查文件的内容,问题进一步变成了“文本”还是“二进制”? 为了简单起见,我们将“文本”限制为可在用户控制台上打印的字符。 特别是你将如何执行这个? (我认为这是暗示在这个网站上,但我想这是有帮助的,一般来说,指出现有的代码,这样做,我应该指定),我不是真的在现有的程序可以用来做什么这个。

我们的软件读取一些二进制文件格式以及文本文件。

我们首先看看我们认识的一个幻数的前几个字节。 如果我们不知道所读取的任何二进制types的幻数,那么我们查看文件的前2K字节,看它是否是UTF-8 , UTF-16或编码的文本文件在主机操作系统的当前代码页中。 如果没有通过这些testing,我们认为它不是我们可以处理的文件,并抛出一个适当的exception。

您可以使用file命令。 它在文件( man file )上做了一堆testing,以确定它是二进制还是文本。 你可以看看/借用它的源代码,如果你需要从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 

您可以使用确定文件的MIMEtypes

 file --mime 

简写是在Linux上的file -i和在macOS上的file -I (大写i)(参见注释)。

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

那么,如果你只是检查整个文件,看看每个字符是否打印与isprint(c) 。 对于Unicode来说它变得更复杂一点。

为了区分一个unicode文本文件, MSDN提供了一些很好的build议来做什么 。

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

 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,您需要手动parsing数据,因为单个字符可以由可变数量的字节表示。 另外,如果你真的是肛门的话,如果在你的平台上可用的话,你会想要使用iswprint的语言环境变体。

Perl有一个体面的启发式。 使用-B运算符来testing二进制(及其相反的, -T来testing文本)。 这里是一个简单的列表文本文件:

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

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

大多数试图区分这种差异的程序都使用启发式的方法,例如检查文件的前n个字节,看看这些字节是否全部符合“文本”的要求(即,它们是否都属于可打印的ASCII字符范围) 。 为了更好地理解,类UNIX系统上总是有“文件”命令。

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

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

这将获得文件types,并与一个沉默的grep,你可以决定是否其文本。

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

如前所述* nix操作系统在文件命令中具有此function。 该命令使用一个configuration文件来定义许多stream行的文件结构中包含的幻数。

这个名为magic的文件历史上存储在/ etc中,尽pipe这可能在某些发行版的/ usr / share中。 魔术文件定义文件中已知存在的值的偏移量,然后可以检查这些位置以确定文件的types。

魔法文件的结构和描述可以通过查阅相关的手册页(man magic)来find,

至于一个实现,可以在file.c中find,但是文件命令的相关部分决定它是否是可读的文本,如下所示

 /* 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 */ } 

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

有许多语言的包装:

  • python
  • 。净
  • 的NodeJS
  • ruby

要在当前目录/子目录中列出文本文件名称:

 $ grep -rIl '' 

二进制文件:

 $ grep -rIL '' 

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

 $ grep -qI '' FILE 

那么退出状态“0”意味着该文件是一个文本; '1' – 二进制。 可以检查:

$ echo $?