2008-11-03 50 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?

相關問題