簡體   English   中英

C - 從文件末尾讀取意外的隨機字符

[英]C - Unexpected random characters being read from end of file

我正在嘗試從 csv 文件中讀取逗號分隔的單詞列表,但在處理由 C 讀入時出現在文件末尾的無縫隨機字符時遇到了麻煩。當我從列表中添加/刪除單詞時,文件似乎完全改變了。

這是文件中包含的內容: johnny,david,alan,rodney,bob,ronald,andrew,hola,goodbye 那是完全復制的,最后沒有意外的空格或回車。

以下是程序讀取的內容:

這是在文本中閱讀的代碼:

    char* name;
    FILE *fp;
    char *fcontent;
    int wordCount = 0;
    char delim = ',';
    long fsize;
    bool end = false;
    char guessedLetters[26];
    int guessNum = 0;
    int lives = 0;

    for (int i = 0; i < 26; i++) {
        guessedLetters[i] = '\0';
    }

    fp = fopen(WORDS_FILENAME, "r");

    if (fp == NULL) {
        printf("Words File Exception: Exiting.");
        return 1;
    }

    fseek(fp, 0L, SEEK_END);
    fsize = ftell(fp);
    fseek(fp, 0L, SEEK_SET);

    fcontent = (char*)calloc(fsize, sizeof(char));

    if (fcontent == NULL) {
        printf("No words in file: Exiting.");
        return 1;
    }

    fread(fcontent, sizeof(char), fsize, fp);
    char *fcontent2 = malloc(strlen(fcontent + 1));
    strcpy(fcontent2, fcontent);
    fclose(fp);

單詞被拆分成單詞數組,流氓字符被保留在最后一個單詞的末尾,導致程序后期出現很多問題。

這是將字符串拆分為數組wordArr的代碼:

char wordArr[wordCount][15];

    char *ptr2 = strtok(fcontent2, &delim);
    int count = 0;

    while (ptr2 != NULL) {
        strcpy(wordArr[count], ptr2);
        count++;
        ptr2 = strtok(NULL, &delim);
    }

也許如果無法完全省略讀取的字符,則可以在拆分過程中省略它們?

謝謝,傑克。

首先,您以文本模式打開文件:

fp = fopen(WORDS_FILENAME, "r");

根據 C 標准7.21.9.4 ftell 函數,第 2 段

ftell 函數獲取流指向的流的文件位置指示符的當前值。 對於二進制流,該值是從文件開頭開始的字符數。 對於文本流,其文件位置指示符包含未指定的信息,可被 fseek 函數用於將流的文件位置指示符返回到 ftell 調用時的位置; 兩個這樣的返回值之間的差異不一定是對寫入或讀取的字符數的有意義的度量。

您不能在文本流上使用ftell()來判斷可能讀取的字節數。

因此,您必須以二進制模式打開文件才能使用ftell() (但請參閱下面的注釋):

fp = fopen(WORDS_FILENAME, "rb");

現在你有文件大小:

fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0L, SEEK_SET);

fcontent = (char*)calloc(fsize, sizeof(char));

但是,這沒有為任何'\\0'終止符留下空間,所以應該是

// no need to cast a void * in C, and sizeof(char)
// is **always** one by definition
fcontent = calloc(fsize + 1 , 1);

現在您將擁有文件內容的終止字符串。

關於二進制流上的fseek()注意事項

根據 C 標准,使用fseek()到達二進制流的末尾實際上是未定義的行為。

根據7.21.9.2 fseek 函數,第 3 段

對於二進制流,新位置(以文件開頭的字符為單位)是通過將偏移量添加到由 wherece 指定的位置獲得的。 如果 wherece 是 SEEK_SET,則指定的位置是文件的開頭,如果是 SEEK_CUR,則是文件位置指示符的當前值,如果是 SEEK_END,則是文件的結尾。 二進制流不需要有意義地支持具有 SEEK_END 值的 fseek 調用。

腳注 268甚至指出:

將文件位置指示符設置為文件結尾,與 fseek(file, 0, SEEK_END) 一樣,對於二進制流(由於可能的尾隨空字符)或任何具有狀態相關編碼的流具有未定義的行為確定以初始換檔狀態結束。

您可以使用fseek(fp, 0L, SEEK_END);的唯一原因fseek(fp, 0L, SEEK_END); 是因為大多數操作系統都擴展了 C 語言並實際定義了它的工作原理。

讀取的數據不包含終止空字符。

您需要檢查讀取的字符數,然后“手動”設置終止空字符:

int cnt = fread(fcontent, sizeof(char), fsize, fp);
fcontent[cnt] = '\0';

當然,好的做法是在將其用作數組索引之前檢查cnt是否為負(讀取錯誤)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM