【问题标题】:How do C's file I/O functions handle NUL characters?C 的文件 I/O 函数如何处理 NUL 字符?
【发布时间】:2012-10-04 08:58:57
【问题描述】:

标准 C 中的文件输入函数,如 fgetc()、fgets() 或 fscanf(),是否对 NUL ('\0') 字符有任何问题,或者将它们与其他字符区别对待?

我想问我是否可以使用 fgets() 来读取可能包含 NUL 字符的行,但我刚刚意识到,由于该函数 NUL 终止输入并且不会以任何其他方式返回长度,无论如何,它对这种用途毫无价值。

我可以使用 fgetc()/getc()/getchar() 代替吗?

【问题讨论】:

  • 读取包含 NUL 字符的 text 文件会调用 GIGO。垃圾进,垃圾出。
  • 请注意,字符的返回类型 (int) 的范围比可返回的值(一个字节)更大。这是有原因的......

标签: c


【解决方案1】:

如果您正在阅读的内容实际上是文本,那么您的处境会有些尴尬。 fgets 将很好地读取 NUL,将它们存储在缓冲区中,然后继续前进。但是,问题是,您刚刚读入的内容不再是 C 库通常期望的 NTBS(NUL-终止字节字符串),因此大多数期望字符串的函数将忽略之后的所有内容第一个 NUL。而且您确实没有可靠的方法来获取长度,因为fgets 不会将其返回给您,而strlen 需要一个C 字符串。 (您可以想象每次都将缓冲区归零并查找最后一个非 NUL 字符以获得长度,但对于大缓冲区中的短字符串,这有点难看。)

如果您处理的是二进制文件,事情就简单多了。您只需 freadfwrite 数据,一切都很好。但是,如果您想要其中包含 NUL 的文本,您可能最终需要自己的 read-a-line 函数来返回长度。

【讨论】:

    【解决方案2】:

    不,输入函数对 NUL 的处理方式与其他字符不同。但是,由于任何返回未知数量的字符都使用 NUL 终止,因此最简单的方法是编写自己的,例如:

    ssize_t myfgets(char *buffer, size_t buffSize, FILE *file) {
        ssize_t count = 0;
        int character;
        while(count < buffSize && (character = getc(file)) != EOF) {
            buffer[count] = character;
            ++count;
            if(character == '\n') break;
        }
        if(count == 0 && character == EOF) return EOF;
        return count;
    }
    

    这个函数类似于fgets,只是它返回读取的字符数并且不以NUL终止字符串。如果您希望字符串以 NUL 结尾,请将 while 循环中的第一个条件更改为 count &lt; buffSize-1 并在循环后添加 buffer[count] = '\0';

    【讨论】:

      【解决方案3】:

      如果您以“TEXT”模式打开文件,则无法读取超出 NULL 字符的文件。但是二进制文件可以是 open()ed、read() 和 close()d。查找这些函数和二进制 i/o。

      此外,EOF 字符在 TEXT 文件中设置为 NULL 字符。但是,您可以使用 fstat 查询二进制文件的大小,并读取二进制数据(可能包含 NULL 字符)

      【讨论】:

      • “二进制”和“文本”模式,AFAIK,只有一个不同:系统可以自动转换换行符。有可能(但很疯狂)在文本文件中有一个 NUL,并在我使用的每个平台上阅读它和所有内容......但是如果你试图将你阅读的内容视为 NUL,你就会开始遇到问题-终止字符串,而不是显式地始终指定长度。
      • 我所说的只是用“rb”打开它。 fopen("文件名.bin","rb"); :)
      • 不过,如果您使用fgets 阅读,则该文件显然是文本。可以肯定的是,如果它包含 NUL,则文本很混乱,但仍然是文本。如果它实际上是一个文本文件,则切换到二进制模式在语义上是不正确的。
      • 另外,切换到二进制模式不会解决fgets 的任何问题。您必须使用 fread,它会忽略换行符,或者 fgetc 一次一个字符。
      • @cHao 你是对的。他不能使用 fgets() fread() 是要走的路。这就是为什么我建议 open()、read() 和 close()。 fread() fopen() 和 fclose() 是它的近亲(和标准)
      猜你喜欢
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      • 2012-07-05
      • 1970-01-01
      • 2020-06-26
      • 1970-01-01
      • 2014-05-27
      • 1970-01-01
      相关资源
      最近更新 更多