10

考虑以下简单的 C 程序,它将文件读入缓冲区并将该缓冲区显示到控制台:

#include<stdio.h>

main()
{
  FILE *file;
    char *buffer;
    unsigned long fileLen;
    //Open file
    file = fopen("HelloWorld.txt", "rb");
    if (!file)
    {
        fprintf(stderr, "Unable to open file %s", "HelloWorld.txt");
        return;
    }
    //Get file length
    fseek(file, 0, SEEK_END);
    fileLen=ftell(file);
    fseek(file, 0, SEEK_SET);
    //Allocate memory
    buffer=(char *)malloc(fileLen+1);
    if (!buffer)
    {
        fprintf(stderr, "Memory error!");
        fclose(file);
        return;
    }
    //Read file contents into buffer
    fread(buffer, fileLen, 1, file);
    //Send buffer contents to stdout
    printf("%s\n",buffer);    
    fclose(file);
}

它将读取的文件仅包含:

你好世界!

输出是:

世界你好!²²²²▌▌▌▌▌▌▌↔☺</p>

自从我在 C/C++ 中做任何重要的事情以来已经有一段时间了,但通常我会假设缓冲区被分配的比必要的大,但情况似乎并非如此。

fileLen 最终为 12,这是准确的。

我现在在想,我必须只是错误地显示缓冲区,但我不确定我做错了什么。

谁能告诉我我做错了什么?

4

5 回答 5

40

您需要 NUL 终止您的字符串。添加

buffer[fileLen] = 0;

在打印之前。

于 2008-11-03T22:45:52.823 回答
28

JesperE 的方法会奏效,但您可能想知道还有另一种处理方法。

即使没有 NUL 终止符,您也可以始终打印已知长度的字符串,方法是提供长度 toprintf作为字符串字段的精度:

printf("%.*s\n", fileLen, buffer);

这允许您在不修改缓冲区的情况下打印字符串。

于 2008-11-03T23:14:40.737 回答
8

JesperE 在您的示例中关于 nul 终止问题是正确的,我只是补充一点,如果您正在处理文本文件,最好使用 fgets() 或类似的东西,因为这将正确处理跨不同平台的换行符序列并且总是为您终止字符串。如果您真的在使用二进制数据,那么您不想使用 printf() 来输出数据,因为 printf 函数需要字符串,并且数据中的 nul 字节会导致输出截断。

于 2008-11-03T23:13:40.643 回答
3

您通过查找文件末尾然后使用来确定文件大小的方法ftell()是错误的:

  • 如果它是一个文本文件,没有"b"fopen()调用的第二个参数中打开,那么ftell()可能不会告诉您可以从文件中读取的字符数。例如,windows 使用两个字节作为行尾,但读取时,它是一个char。事实上,ftell()在文本模式下打开的流的返回值仅在调用时有用fseek(),而不是确定文件大小。
  • 如果它是一个二进制文件,"b"在第二个参数 to 中打开fopen(),那么 C 标准有这样的说法:

    将文件位置指示符设置为文件结尾,与 一样fseek(file, 0, SEEK_END),对于二进制流(因为可能出现尾随空字符)或任何具有状态相关编码但不能确保在初始移位状态结束的流具有未定义的行为。

所以,你所做的不一定能在标准 C 中工作。你最好的选择是使用fread()阅读,如果你碰巧需要更多内存,使用realloc(). 您的系统可能会提供mmap()或保证将文件位置指示符设置为二进制流的文件结尾——但依赖这些是不可移植的。

另请参阅此 C-FAQ:文本和二进制 I/O 有什么区别?.

于 2010-01-16T03:06:35.710 回答
0

您可以使用calloc而不是malloc分配已初始化的内存。calloc承担额外的论点。它对于分配数组很有用;的第一个参数calloc表示要为其分配内存的数组中元素的数量,第二个参数是每个元素的大小。由于 a 的大小char始终为 1,我们可以将1其作为第二个参数传递:

 buffer = calloc (fileLen + 1, 1);

在 C 中,不需要强制转换mallocor的返回值calloc。以上将确保即使文件读取因任何原因提前结束,字符串也会以空值终止。calloc确实需要更长的时间,malloc因为它必须在给你之前将你要求的所有内存清零。

于 2010-01-16T02:56:06.120 回答