2008-11-03 65 views
8

考虑一个文件读入缓冲区,并显示该缓冲区到控制台下面这个简单的C程序:为什么我的简单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); 
} 

该文件将读取只包含:

的Hello World!

输出是:

的Hello World²²²²▌▌▌▌▌▌▌↔☺

,因为我做了什么在C/C显著它已经有一段时间++,但通常我会认为缓冲区被分配得比所需要的大,但这似乎并不是这样。

fileLen结束为12,这是准确的。

我现在在想,我必须只是显示缓冲区错误,但我不知道我在做什么错误。

任何人都可以告诉我我做错了什么吗?

回答

38

您需要NUL终止您的字符串。在打印之前添加

buffer[fileLen] = 0; 

+1

取-1,你是对的! – GEOCHET 2008-11-03 22:48:33

+0

我曾试图在某一时刻做到这一点,但出于某种原因,它看起来像我有一个大脑放屁,并使用\ n而不是\ 0为一些愚蠢的原因。告诉你我是生锈的! – GEOCHET 2008-11-03 22:51:08

+0

我错过了缓冲区是fileLen + 1字节长... – JesperE 2008-11-03 22:52:08

8

JesperE对于你的例子中的nul-termination问题是正确的,我只是补充说,如果你正在处理文本文件,最好使用fgets()或类似的东西,因为这将正确处理跨越不同的新行序列平台,并将永远不会终止你的字符串。如果您确实在使用二进制数据,那么您不希望使用printf()来输出数据,因为printf函数需要字符串,并且数据中的一个nul字节将导致输出被截断。

28

JesperE的方法可行,但您可能有兴趣知道有一种处理此问题的替代方法。

您可以随时打印已知长度的字符串,即使没有NUL终止符,通过提供长度printf的精度为字符串字段:

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

这允许您打印字符串无修改缓冲区。

0

您可以使用calloc而不是malloc来分配已经初始化的内存。 calloc需要额外的参数。这对分配数组非常有用; calloc的第一个参数表示数组中要为其分配内存的元素数量,第二个参数是每个元素的大小。由于char的大小始终是1,我们就可以通过1作为第二个参数:

buffer = calloc (fileLen + 1, 1); 

在C,没有必要以浇注malloccalloc的返回值。以上将确保即使文件的读取由于某种原因而过早结束,字符串也将被终止。calloc确实需要比malloc更长的时间,因为它在给你之前必须将你要求的所有内存清零。

2

你的方法寻求到文件的末尾,然后用ftell()来确定文件的大小是错误的:

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

    设置文件位置指示器到档案结尾,与fseek(file, 0, SEEK_END),对于二进制流(由于可能的结尾空字符)或对于具有状态依赖性编码的任何流而言未定义的行为,其不确定地以初始转换状态结束。

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

另请参阅本C-FAQ:What's the difference between text and binary I/O?

相关问题