如何将文件内容识别为 ASCII 或二进制

Posted

技术标签:

【中文标题】如何将文件内容识别为 ASCII 或二进制【英文标题】:How to identify the file content as ASCII or binary 【发布时间】:2010-09-21 14:36:05 【问题描述】:

如何使用 C++ 识别文件内容是 ASCII 还是二进制?

【问题讨论】:

其他问题更好,所以建议关闭这个。重复***.com/questions/567757/… 【参考方案1】:

如果文件只包含十进制字节 9–13、32–126,则它可能是纯 ASCII 文本文件。否则,它不是。但是,它可能仍然是另一种编码的文本。

如果在除了上述字节之外,该文件包含十进制字节 128–255,它可能是一个 8 位或变量的文本文件 -长度 基于 ASCII 的编码,例如 ISO-8859-1、UTF-8 或 ASCII+Big5。如果不是,出于某些目的,您可能可以在这里停下来并认为该文件是二进制文件。但是,它可能仍然是 16 位或 32 位编码的文本。

如果文件不符合上述限制,请检查文件的前 2-4 个字节是否有 byte-order mark:

如果前两个字节是十六进制 FE FF,则文件暂时是 UTF-16 BE。 如果前两个字节是十六进制FF FE,而后面两个字节不是十六进制00 00,则文件暂定为UTF-16 LE。 如果前四个字节是十六进制 00 00 FE FF,则文件暂时是 UTF-32 BE。 如果前四个字节是十六进制 FF FE 00 00,则文件暂时是 UTF-32 LE。

如果通过上面的检查,你已经确定了一个暂定编码,那么只检查下面对应的编码,以确保该文件不是碰巧匹配字节顺序标记的二进制文件。

如果您尚未确定暂定编码,则该文件可能仍是其中一种编码的文本文件,因为字节顺序标记不是强制性的,因此请检查以下列表中的所有编码:

如果文件包含十进制值为 9-13、32-126 和 128 或以上的大端双字节字,则该文件可能是 UTF-16 BE。李> 如果文件包含 little-endian 两字节字,十进制值为 9–13、32–126 和 128 或更高,则该文件可能是 UTF-16 LE。李> 如果文件包含十进制值为 9–13、32–126 和 128 或以上的大端四字节字,则该文件可能是 UTF-32 BE。李> 如果文件包含小端四字节字,十进制值为 9–13、32–126 和 128 或更高,则该文件可能是 UTF-32 LE。李>

如果在所有这些检查之后,您仍然没有确定编码,则该文件不是我所知道的任何基于 ASCII 编码的文本文件,因此对于大多数用途,您可能会认为它是二进制文件(它可能仍然是非 ASCII 编码(例如 EBCDIC)的文本文件,但我怀疑这远远超出了您的关注范围)。

【讨论】:

这仅适用于文本为 ASCII 的情况。如果是 UTF16 或 UTF32,那么它可能包含值为 0-8、14-31 和 127 的字节。因此您的答案令人困惑。 @David Arno,没错,但问题实际上是关于 ASCII 与否。 @quinmars,我提请您注意这个答案的第一行“我假设您真的想检测文件是否是文本(以任何编码),而不仅仅是 ASCII。”。鉴于此,第二行是完全错误的。因此,答案令人困惑和误导。 @David Arno:我同意,所以我编辑了我的答案以反映您的 cmets。谢谢:)。 抱歉,Daniel,但系统不允许我撤消我的反对票,这很荒谬,因为您已经对其进行了编辑以使其成为一个非常好的答案:(【参考方案2】:

您使用带有stream.get() 的普通循环遍历它,并检查您读取的字节值是否为<= 127。多种方法中的一种:

int c;
std::ifstream a("file.txt");
while((c = a.get()) != EOF && c <= 127) 
    ;
if(c == EOF) 
    /* file is all ASCII */

但是,正如有人提到的,所有文件毕竟都是二进制文件。此外,不清楚您所说的“ascii”是什么意思。如果你的意思是字符代码,那么这确实是你要走的路。但是,如果您仅指字母数字值,则需要另一种方法。

【讨论】:

我认为这不是作者的本意。但事实上这是正确的答案。 :-) 这是所提问题的正确答案。然而,Tomalak 你说得对,san 可能没有正确表达这个问题。 “ASCII 或二进制”这一表达暗示他的真正意思是“文本,而不是二进制”。 顺便说一句:“字母数字”只是文本的子集。 是的。也许他想要那个。但也许他也想包含 '['... 永远不知道 :)【参考方案3】:

我的文本编辑器决定是否存在空字节。在实践中,这非常有效:没有空字节的二进制文件极为罕见。

【讨论】:

这也是 gnu diff 所做的。除了他们只查看文件中的预定义长度。 (不想为空字节浏览 4GB 文件...) 这也是“grep -I”的作用。【参考方案4】:

每个文件的内容都是二进制的。所以,其他什么都不知道,你无法确定。

ASCII 是一个解释问题。如果你在文本编辑器中打开一个二进制文件,你就会明白我的意思。

大多数二进制文件都包含一个固定的标题(每种类型),您可以查找,或者您可以将文件扩展名作为提示。如果您需要 UTF 编码的文件,您可以查找字节顺序标记,但它们也是可选的。

除非你更仔细地定义你的问题,否则不可能有明确的答案。

【讨论】:

【参考方案5】:

看看file command 是如何工作的;它有三种策略来确定文件的类型:

文件系统测试 magic number 测试 和语言测试

根据您的平台以及您可能感兴趣的文件,您可以查看它的实现,甚至调用它。

【讨论】:

【参考方案6】:

如果问题真的是如何仅检测 ASCII,那么 litb 的答案就是正确的。但是,如果 san 在知道如何确定文件是否包含文本之后,那么问题就会变得更加复杂。 ASCII 只是一种 - 越来越不受欢迎 - 表示文本的方式。 Unicode 系统 - UTF16、UTF32 和 UTF8 越来越受欢迎。从理论上讲,可以通过检查前两个字节是否为 unicocode 字节顺序标记 (BOM) 0xFEFF(如果字节顺序颠倒,则为 0xFFFE)来轻松测试它们。然而,由于这两个字节搞砸了 Linux 系统的许多文件格式,因此不能保证它们存在。此外,二进制文件可能以 0xFEFF 开头。

如果文件是 unicode,查找 0x00(或其他控制字符)也无济于事。如果文件是 UFT16,并且文件包含英文文本,那么每隔一个字符将是 0x00。

如果您知道将写入文本文件的语言,则可以分析字节并统计确定它是否包含文本。例如,英语中最常见的字母是 E 后跟 T。因此,如果文件包含的 E 和 T 比 Z 和 X 多得多,那么它很可能是文本。当然,有必要将其作为 ASCII 和各种 unicode 进行测试以确保。

如果文件不是用英文编写的——或者你想支持多种语言——那么剩下的两个选项就是查看 Windows 上的文件扩展名,并根据“magic file”数据库检查前四个字节" 代码来确定文件的类型,从而确定它是否包含文本。

【讨论】:

【参考方案7】:

嗯,这取决于您对 ASCII 的定义。您可以检查 ASCII 码

您还可以检查常规换行符(0x10 或 0x13,0x10)以检测文本文件。

【讨论】:

【参考方案8】:

要检查,您必须以二进制形式打开文件。您无法将文件作为文本打开。 ASCII 实际上是二进制的子集。 之后,您必须检查字节值。 ASCII 的字节值是 0-127,但 0-31 是控制字符。 TAB、CR 和 LF 是唯一常用的控制字符。 你不能(便携式)使用'A'和'Z';不能保证它们是ASCII(!)。 如果你需要它们,你必须定义。

const unsigned char ASCII_A = 0x41; // NOT 'A'
const unsigned char ASCII_Z = ASCII_A + 25;

【讨论】:

【参考方案9】:

这个问题确实没有正确或错误的答案,只是复杂的解决方案不适用于所有可能的文本文件。

这是一个链接The Old New Thing Article,说明记事本如何检测 ascii 文件的类型。它并不完美,但看看微软如何处理它很有趣。

【讨论】:

【参考方案10】:

Github's linguist 使用charlock holmes library 检测二进制文件,而二进制文件又使用ICU 的charset detection。

ICU 库可用于多种编程语言,包括 C 和 Java。

【讨论】:

【参考方案11】:
bool checkFileASCIIFormat(std::string fileName)

    bool ascii = true;
    std::ifstream read(fileName);
    int line;
    while ((ascii) && (!read.eof())) 
        line = read.get();
        if (line > 127) 
            //ASCII codes only go up to 127
            ascii = false;
        
    

    return ascii;

【讨论】:

以上是关于如何将文件内容识别为 ASCII 或二进制的主要内容,如果未能解决你的问题,请参考以下文章

PowerShell 该术语未被识别为 cmdlet 函数脚本文件或可运行程序

解决VScode安装Babel转码器报错:无法将“cnpm“项识别为cmdlet函数脚本文件或可运行程序的名称的问题

无法将“slmgr”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。如何解决?

我可以让 git 将 UTF-16 文件识别为文本吗?

tsc : 无法将“tsc”项识别为 cmdlet函数脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

避免使用小数。TryParse 将“1.1.1”或“1,1,1”识别为十进制