72

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

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

但是,有各种工具可以处理各种文件,实际上,您希望根据文件是“文本”还是“二进制”来做一些不同的事情。这方面的一个例子是任何在控制台上输出数据的工具。纯“文本”看起来不错,而且很有用。“二进制”数据会弄乱您的终端,而且通常没有用处。GNU grep 在确定是否应该将匹配项输出到控制台时至少使用了这种区别。

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

4

11 回答 11

64

您可以使用该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
于 2009-02-19T23:54:20.287 回答
18

您可以确定文件的MIME 类型

file --mime FILENAME

简写file -i在 Linux 上和file -I(大写 i)在 macOS 上(见评论)。

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

于 2009-02-19T23:55:50.627 回答
17

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

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

于 2009-02-20T01:10:00.417 回答
6
  • 列出当前目录/子目录中的文本文件名:

    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.
于 2017-04-24T10:47:00.473 回答
4

好吧,如果您只是检查整个文件,请查看是否每个字符都可以使用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你的平台上是否可用的语言环境变体。

于 2009-02-19T23:51:10.717 回答
3

Perl 有一个不错的启发式算法。使用-B运算符来测试二进制(反之,-T测试文本)。这是一个列出文本文件的单行shell:

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

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

于 2013-01-30T12:00:55.850 回答
3

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

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

这将获得文件类型,并且使用静默 grep 您可以确定它是否为文本。

于 2015-05-23T20:59:35.543 回答
3

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

有许多语言的包装器:

于 2016-06-23T14:24:27.580 回答
2

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

于 2009-02-19T23:52:22.430 回答
1

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

于 2009-02-20T00:59:21.737 回答
1

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

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

魔术文件的结构和描述可以通过查阅相关手册页(man magic)找到

至于实现,可以在 file.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 */
    }
于 2009-02-20T01:47:57.300 回答