我如何区分“二进制”和“文本”文件?
非正式地,我们大多数人都明白,有“二进制”文件(目标文件,图像,电影,可执行文件,专有文件格式等)和“文本”文件(源代码,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 $?