29

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

4

11 回答 11

55

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

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

如果文件不符合上述约束,请检查文件的前 2-4 个字节是否有字节顺序标记

  • 如果前两个字节是 hex FE FF,则文件暂定为UTF-16 BE。
  • 如果前两个字节是 hex FF FE,而后两个字节不是hex 00 00,则文件暂定为UTF-16 LE。
  • 如果前四个字节是 hex 00 00 FE FF,则文件暂定为UTF-32 BE。
  • 如果前四个字节是 hex FF FE 00 00,则文件暂定为UTF-32 LE。

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

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

  • 如果文件包含大端双字节字,十进制值为 9–13、32–126 和 128 或更高,则该文件可能是 UTF-16 BE。
  • 如果文件包含小端的双字节字,十进制值为 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,但我怀疑这远远超出了您的关注范围)。

于 2008-11-10T10:40:43.563 回答
17

您使用带有 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”是什么意思。如果你的意思是字符代码,那么这确实是你要走的路。但是,如果您仅指字母数字值,则需要另一种方法。

于 2008-11-10T10:26:04.873 回答
12

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

于 2008-11-10T10:54:27.123 回答
10

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

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

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

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

于 2008-11-10T10:24:56.810 回答
10

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

  • 文件系统测试
  • 幻数测试
  • 和语言测试

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

于 2008-11-10T10:43:04.790 回答
8

如果问题真的是如何只检测 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 上的文件扩展名,并根据“魔术文件”代码的数据库检查前四个字节以确定文件的类型,从而确定它是否包含文本。

于 2008-11-10T11:35:10.257 回答
1

好吧,这取决于您对 ASCII 的定义。您可以检查 ASCII 代码 <128 的值或您定义的某些字符集(例如 'a'-'z'、'A'-'Z'、'0'-'9'...)并处理文件如果它包含一些其他字符,则为二进制。

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

于 2008-11-10T10:22:59.590 回答
1

要检查,您必须将文件作为二进制文件打开。您无法将文件作为文本打开。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;
于 2008-11-10T10:55:13.770 回答
1

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

这是关于记事本如何检测 ascii 文件类型的旧新事物文章的链接。它并不完美,但看看微软如何处理它很有趣。

于 2008-11-18T06:06:33.140 回答
0

Github 的语言学家使用charlock holmes 库来检测二进制文件,而二进制文件又使用ICUcharset 检测

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

于 2015-03-23T17:25:04.347 回答
-1
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;
}
于 2021-07-30T03:36:47.527 回答