Unix 我如何区分';二进制';和';文本';文件夹?

Unix 我如何区分';二进制';和';文本';文件夹?,unix,language-agnostic,ascii,binaryfiles,file-format,Unix,Language Agnostic,Ascii,Binaryfiles,File Format,非正式地说,我们大多数人都知道有“二进制”文件(对象文件、图像、电影、可执行文件、专有文档格式等)和“文本”文件(源代码、XML文件、HTML文件、电子邮件等) 一般来说,您需要知道文件的内容才能对它做任何有用的事情,并且形成这样的观点:如果编码是“二进制”或“文本”,那么这并不重要。当然,文件只存储字节数据,所以它们都是“二进制”的,“文本”在不知道编码的情况下没有任何意义。然而,谈论“二进制”和“文本”文件仍然是有用的,但为了避免用这种不精确的定义冒犯任何人,我将继续使用“恐吓”引号 但是,

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

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

但是,有各种工具可以处理范围广泛的文件,实际上,您希望根据文件是“文本”还是“二进制”来执行不同的操作。例如,在控制台上输出数据的任何工具。纯“文本”看起来不错,而且很有用二进制数据会把你的终端弄乱,而且通常是没有用的。GNUGREP在确定是否应该向控制台输出匹配项时,至少使用了这种区别


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

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

要区分unicode文本文件

其要点是首先检查最多前四个字节:

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

大多数试图区分差异的程序都使用启发式方法,比如检查文件的前n个字节,看看这些字节是否都符合“文本”的条件(即,它们是否都在可打印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
您可以使用

file --mime FILENAME
Linux上的缩写是
file-i
,macOS上的缩写是
file-i
(大写i)(参见注释)


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

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

我公司生产的电子表格软件可以读取f二进制文件格式以及文本文件


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

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

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

可以通过查阅相关手册页面(man magic)找到magic文件的结构和说明

对于一个实现,可以在其自身中找到,但是确定其是否为可读文本的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 */
    }
/*在查找令牌之前,请确保我们正在处理ascii文本*/
对于(i=0;i
Perl有一个不错的启发式方法。使用
-B
操作符测试二进制文件(与其相反的是,
-T
测试文本文件)。下面是一行代码来列出文本文件:

$ 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
,这是Unix
文件
命令行的库版本

许多语言都有包装纸
$ grep -rIl ''
$ grep -rIL ''
$ grep -qI '' FILE