2012-03-29 24 views
2

我是一位正在進行的C程序員,剛開始覺得我有些事情是正確的。習慣於更高級別的語言(如C#和Python),我真的很想念我的例外。該功能應該做什麼錯誤檢查?

以下是我編寫和正在使用的函數來查看流X字符數。

該功能可以做什麼錯誤檢查,以確保它不會制動程序?

#include <stdio.h> 
#include <string.h> 

#include "speek.h" 

void speek(char *peek, size_t len, FILE *stream) { 

    int i; 

    for (i = 0; i < len; i++) { 
     peek[i] = fgetc(stream); 
     if (feof(stream) || i >= len - 1) { 
     // Null terminate the string if stream is EOF or requested number 
     // of characters has already been fetched. 
     peek[i] = '\0'; 
     break; 
     } 
    } 

    for (i = strlen(peek); i >= 0; i--) { 
     ungetc(peek[i], stream); 
    } 
} 
+0

這是* C的*困難的部分:你必須提前決定你如何去檢查並報告錯誤,並且您既沒有例外,也沒有堆棧展開。對於無法從中恢復的I/O故障,我建議使用錯誤消息明確停止該程序。對於本身的問題,每個I/O函數都可能隨時失敗(例如,假設文件在USB密鑰上,並且在程序運行時將其刪除),因此應該檢查每個*函數是否正確執行。 – 2012-03-29 20:02:00

回答

1

我會在閱讀字符時總是檢查FILE *。

我認爲這是一種風格的東西,但我不會把我通常打算髮生在循環內的東西。

最後,成功讀取的字符串應始終以'\ 0'結尾。當出現錯誤時,我經常會對做什麼事情感到厭倦。如果我知道功能是什麼,我通常可以選擇。

因此,使用你的文件檢查的風格:

void speek(char *peek, size_t len, FILE *stream) { 

    int i; 
    for (i = 0; i < len-1 && !feof(stream); i++) { 
     peek[i] = fgetc(stream); 
    } 

    peek[i] = '\0'; 

    for (i = strlen(peek)-1; i >= 0; i--) { 
     if (ungetc(peek[i], stream) != peek[i]) { 
     break; // I'd return an error, but function is void 
     } 
    } 
} 

如果這不是一個快速的實驗多,還是我在寫這個的人,我可能會使用斷言檢查參數(是的,我知道,我還是做了(C = GETC(...)),我老了)

#include <assert.h> 

int speek(char *peek, size_t len, FILE *stream) { 
    assert(peek != NULL); 
    assert(len > 0); /* Could 0 be okay? */ 
    assert(stream != NULL && !ferror(stream)); 

    int i; 
    int c; 
    for (i = 0; i < len-1 && (c=getc(stream)) != EOF; i++) { 
     peek[i] = c; 
    } 

    peek[i] = '\0'; 

    if (ferror(stream)) return -1; 

    for (int j = i-1; j >= 0; j--) { 
     if (ungetc(peek[j], stream) != peek[j]) { 
     return -1; 
     } 
    } 
    return i; /* I like to return something useful */ 
} 

編輯:我試過鏡原來的,但它不是我做的方式,所以最後放棄了,寫下了我通常想寫的東西。

編輯2:...但我犯了一個錯誤。正確的方法是在之前檢查字符是否有空間。衛生署!

簡單的測試程序:

int main (int argc, const char * argv[]) { 
    char line0[100]; 
    int n0 = speek(line0, 4, stdin); 

    fprintf(stderr, "%d '%s'\n", n0, line0); 

    char line1[100]; 
    int n1 = speek(line1, 8, stdin); 

    fprintf(stderr, "%d '%s'\n", n1, line1); 

    return 0; 
} 

僅給出abcefghij包含文件提供

3 'abc' 
7 'abcdefg' 
+0

斷言在這裏是完全正確的,因爲失敗的斷言指示程序中的錯誤。 – 2012-03-29 20:04:08

+1

@Alexandre C - 是的。我認爲,特別是當人們學習C時,斷言是一種有用的習慣; '斷言臭蟲死亡'。它給出了加速調試的錯誤的文件名和行號。這些斷言失敗可能是調用者的錯誤,但是,在學習時,它是'我自己的代碼',但它仍然是一個錯誤,這可能是非常令人沮喪的發現。 – gbulmer 2012-03-29 20:17:03

+0

謝謝你的出色答案,儘管這裏缺少一些東西。你的功能,也可能是我原來的功能,拉過一個字符太多而沒有推回去。我解決了它,但恐怕它不再優雅。你會怎麼做? – frklft 2012-03-29 20:56:04

0

添加到什麼@Thiruvalluvar指出,

  1. 如果該文件是空的一個peek將只包含一個NULL字符。 i = strlen(peek)將返回0,但您仍然進入循環,因爲您有i>=0,並嘗試將字符推回到您從此沒有得到的流中。即使len爲零,也會導致相同的結果!

  2. 還有一個是你沒有監視ungetc的返回值。失敗時可能會返回EOF

0

除了檢查NULLif (len > 0)我建議你檢查FILE *是一個有效的指針。您可以將它傳遞給fileno()並檢查它是否返回與-1不同的值。

1

由於Pavan Manjunath表示,您的程序中存在錯誤。至少還有一個我可以立即看到的是,你不處理文件中的NUL字符:在你使用strlen函數的末尾,它只能測量你對第一個NUL字節的刺激,但不一定與您實際閱讀的字節數相同。

但這是一般的錯誤。你問到具體的錯誤檢查。沒有人提到檢查錯誤的最重要的地方:如果讀取文件時出現錯誤。您應該檢查EOF的返回值fgetc(),如其聯機幫助頁(首選)中所述,或者使用ferror()函數來測試是否發生讀取文件錯誤。

檢查peekstream NULL作爲Thiruvalluvar建議是防禦性的,但我不會說是必要的。那些不應該是NULL。如果他們是,這是你的來電者的錯,不是你的。

+1

*每個* I/O操作都可能失敗。您應該檢查*每個* I/O函數調用是否按預期工作。 – 2012-03-29 20:06:50