2014-10-01 41 views
1

對於大多數人來說,看起來似乎是一個愚蠢的問題,但我仍然試圖確定最終答案。幾個小時前,我決定用fgets()替換項目中的所有scanf()函數,以獲得更健壯的代碼。 我瞭解到,與fgets()自動結束插入輸入字符串以「\ n」和的NULL字符,但.. 讓我們說我有這樣的事情:fgets()函數會附加超過最大長度的 n 0個字符嗎?

char user[16]; 

16字符數組哪些商店一個用戶名(最多15個字符,我保留NUL終止符的最後一個)。 現在的問題是:如果我插入15個字符的字符串,那麼'\ n'會在數組的最後一個單元中結束,但NUL終止符是什麼? '\ 0'是否存儲在以下內存塊中? (調用printf()函數時沒有分段錯誤意味着插入的字符串實際上是NUL終止的,對嗎?)。

+2

的'\ N'將在這種情況下,在流中被留。它將在那裏供您下次閱讀。 – 2014-10-01 21:12:03

+1

你的緩衝區應該聲明爲'char user [16];'。 – 5gon12eder 2014-10-01 21:14:42

+0

恩,對不起傢伙!我的錯! :) – Atlas80b 2014-10-01 21:19:16

回答

5

作爲5gon12eder答案的補充。我假設你有這樣的:

char user[16]; 

fgets(user, 16, stdin); 

和你輸入abcdefghijklmno\n,這是15個字符和一個換行符。

fgets將投入user 15(16-1)先輸入後面是空的字符,你會得到有效"abcdefghijklmno",這是你想要的

...但是... \n仍停留在數據流緩衝區實際上可用於下一次讀取(無論是fgets還是其他任何文件)。更確切地說,直到你做另一個fgets,你不知道是否有其他字符在o之後。

+0

+1我的回答確實有點簡潔,可以這麼說。 – 5gon12eder 2014-10-01 21:27:10

+0

現在全部清除,非常感謝您的示例。 我運行了一些其他測試,並最終了解如何正確處理fgets()函數! – Atlas80b 2014-10-01 22:07:29

2

fgetsman page

char *fgets(char *s, int size, FILE *stream); 

fgets()至多一個小於尺寸從字符,並將它們存儲到緩衝器指向小號讀取英寸讀取在EOF或換行符後停止。如果讀取換行符,則將其存儲到緩衝區中。終止空字節('\0')存儲在緩衝區中的最後一個字符之後。

我覺得這很清楚,不是嗎?

+0

我正在使用在線手冊和fgets的定義是不同的.. 現在我覺得很愚蠢,哈哈。謝謝! – Atlas80b 2014-10-01 22:03:51

2
從C99標準(N1256)

文檔的fgets

7.19.7.2的與fgets起作用

梗概

#include <stdio.h> 
char *fgets(char * restrict s, int n, 
FILE * restrict stream); 

說明

fgets函數讀取比n 指定從流指向stream到陣列通過s指出的字符數少至多一個。沒有額外的 字符在換行符(保留)或文件結束後讀取。 A null字符是在讀入數組的最後一個字符後立即寫入的。

作曲到您的文章,你說:

16字符存儲用戶名的數組(最多15個字符,我保留了最後一位爲NUL終止)。問題是:如果我插入一個15個字符的字符串,那麼'\ n'會在數組的最後一個單元中結束,但NUL終止符是什麼?

對於這種情況,直到下次調用fgets或其他任何從流中讀取的調用時纔會讀取換行符。

'\ 0'是否存儲在下面的內存塊中? (調用printf()函數時沒有分段錯誤意味着插入的字符串實際上是NUL終止的,對嗎?)。

始終設置終止空字符。在你的情況下,第16個字符將是終止空字符。

+0

您鏈接爲「fgets'文檔」的網站不具有權威性。 – 2014-10-01 21:18:44

+0

@R。對,現在我記得。他們有這個關於fgets的重要缺陷報告,在最終版本之前的巴塞羅那會議上最後一次採取行動。謝謝你指出,這是有幫助的。 – 2014-10-01 21:51:37

+0

非常感謝你! 所以我還需要在使用新的fgets() – Atlas80b 2014-10-01 22:11:10

2

正如@ 5gon12eder表明,使用方法:

char user[16]; 
fgets(user, sizeof user, stdin); 

// Function prototype for reference 
#include <stdio.h> 
char *fgets(char * restrict s, int n, FILE * restrict stream); 

現在對於細節:

  1. '\n''\0'自動追加。僅自動附加。 fgets()一旦得到'\n'就會停止讀取,但由於其他原因(包括完整緩衝區)也會停止讀取。在這些情況下,'\0'之前沒有'\n'

  2. fgets()不讀取C字符串,而是讀取一行。輸入流通常處於文本模式,然後發生行尾翻譯。在某些系統上,'\r''\n'對將翻譯爲'\n'。在別人身上,它不會。通常讀取的文件與該翻譯相匹配,但發生異常。在二進制模式下,不會發生翻譯。

  3. fgets()讀入'\0'。並繼續閱讀。因此使用strlen(buf)並不總是反映讀取的真實數量char。當位於中間時,可能有一個全面的方法來確定char的真實數字,但它可能更易於使用fread()fgetc()進行編碼。

  4. 在EOF條件(並且沒有數據讀取)或IO錯誤時,fgets()返回NULL。發生I/O錯誤時,緩衝區的內容未定義。

  5. 迂腐問題:C標準使用int類型作爲緩衝區的大小,但通常代碼會傳遞類型爲size_t的變量。大小爲n小於1或大於INT_MAX可能是個問題。 1 的尺寸應該只填充buf[0] = '\0',但某些系統的行爲不同,特別是如果EOF條件接近或通過。但只要2 <= n <= INT_MAX,終止'\0'可以預料。注意:當尺寸太小時,fgets()可能會返回NULL

  6. 代碼通常喜歡刪除終止'\n'與可能會導致麻煩的東西。建議:

    char buf[80]; 
    if (fgets(buf, sizeof buf, stdin) == NULL) Handle_IOError_or_EOF(); 
    
    // IMO potential UB and undesired behavior 
    // buf[strlen(buf)-1] = '\0'; 
    
    // Suggested end-of-line deleter 
    size_t len = strlen(buf); 
    if (len > 0 && buf[len - 1] == '\n') buf[--len] = '\0'; 
    
  7. 健壯的代碼檢查從fgets()的返回值。以下方法有缺點。 1)如果發生IO錯誤,則未定義緩衝區內容。檢查緩衝區內容不會提供可靠的結果。 2)A '\0'可能已經第一個char讀取並且該文件不處於EOF狀態。

    // Following is weak code. 
    buf[0] = '\0'; 
    fgets(buf, sizeof buf, stdin); 
    if (strlen(buf) == 0) Handle_EOF(); 
    
    // Robust, but too much for code snippets 
    if (fgets(buf, sizeof buf, stdin) == NULL) { 
        if (ferror(stdin)) Handle_IOError(); 
        else if (feof(stdin)) Handle_EOF(); 
        else if (sizeof buf <= 1) Handle_too_small_buffer(); // pedantic check 
        else Hmmmmmmm(); 
    } 
    
+0

我已經實現了行尾刪除程序,但是感謝您提供關於EOF/IO錯誤的提示,這非常有用^^ – Atlas80b 2014-10-01 22:00:47

相關問題