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

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

通常,您需要知道文件的内容,以便能够对其进行任何有用的操作,如果编码是“二进制”或“文本”,则形成这种观点并不重要。当然,文件只是存储字节数据,所以它们都是“二进制”的,而“文本”在不知道编码的情况下没有任何意义。然而,谈论“二进制”和“文本”文件仍然是有用的,但为了避免用这种不精确的定义冒犯任何人,我将继续使用“惊吓”引号。

但是,有各种各样的工具可以处理范围很广的文件,实际上,您希望根据文件是“文本”还是“二进制”来执行不同的操作。这方面的一个例子是在控制台上输出数据的任何工具。简单的“文本”看起来不错,而且很有用。“二进制”数据会把你的终端搞乱,通常看起来没什么用。GNUgrep 至少在确定是否应该将匹配输出到控制台时使用这种区别。

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

51248 次浏览

好吧,如果你只是检查整个文件,看看是否每个字符是可打印的 isprint(c)。对于 Unicode,它变得稍微复杂一些。

为了区分 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的 locale 变体,那么您将希望使用它。

大多数尝试区分这种差异的程序都使用启发式方法,比如检查文件的第一个 N字节,看看这些字节 所有是否符合“ text”(也就是说,它们是否都属于可打印的 ASCII 字符范围)。为了更好地区分,类 UNIX 系统上总是有“ file”命令。

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

可以确定文件的 MIME 类型

file --mime FILENAME

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

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

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

我们公司生产的电子试算表可以读取大量的二进制文件格式和文本文件。

我们首先看看我们认识的 神奇的数字的前几个字节。如果我们不能识别我们读取的任何二进制类型的神奇数字,那么我们查看文件的前2K 字节,看看它是一个 UTF-8UTF-16还是一个编码在主机操作系统的当前 密码页中的文本文件。如果它没有通过这些测试,我们假设它不是我们可以处理的文件,并抛出适当的异常。

如前所述 * nix 操作系统在 file 命令中具有这种能力。此命令使用一个配置文件,该文件定义许多流行文件结构中包含的神奇数字。

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

魔术文件的结构和说明可以通过查阅相关手册页(人魔术)找到

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

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

Perl 有一个不错的启发式方法。使用 -B操作符测试二进制文件(相反,使用 -T测试文本)。下面是 shell 一行程序列出的文本文件:

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

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

这是一个老话题,但也许有人会发现这个有用。 如果你必须在一个脚本中决定某些东西是否是一个文件,那么你可以简单地这样做:

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

这将得到文件类型,通过使用静默 grep,您可以判断它是否为文本。

您可以使用 libmagic,它是 Unixfile命令行的库版本。

有许多语言的包装纸:

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

    grep -rIl ''
    
  • 二进制文件:

    grep -rIL ''
    
  • 检查特定文件:

    grep -qI '' FILE
    

    然后,退出状态’0’意味着文件是一个文本;’1’-二进制。 检查:

    echo $?
    

关键的选择是:

  -I     Process a binary file as if it did not contain matching data;

其他选择:

  -r, --recursive
Read all files under each directory, recursively;
-l, --files-with-matches
Suppress normal output; instead print the name of each input file from which output would normally have been printed.
-L, --files-without-match
Suppress normal output; instead print the name of each input file from which no output would normally have been printed.
-q, --quiet, --silent
Quiet; do not write anything to standard output.  Exit immediately with zero status if any match is found, even if an error was detected.