2017-02-28 32 views
1

我很新的C,我曾經在Python工作,我試圖看看我讀的東西是整數。如果沒有,閱讀,直到我設法輸入一個數字。閱讀,直到我設法輸入一個整數

我做了一些研究,發現函數scanf實際上返回1,如果讀取適當,則返回0,否則返回0。 所以,我寫了這個代碼,我不明白爲什麼這是一個無限循環,寫在控制檯上「給一個整數」

#include <stdio.h> 

int main() { 
    int a; 
    int b = 1; 
    do { 
     printf("Give an integer\n"); 
     b = scanf("%d", &a); 
    } while (b == 0); 
} 
+0

[intigers are wonderful things!](https://www.youtube.com/watch?v=dJFyz73MRcg) – user4581301

+3

問題是'scanf()'不會消耗與您的字段描述符無法匹配的數據。因此,當您循環再試時,相同的不匹配數據仍在等待處理。 –

+0

它的工作原理!但我真的不明白爲什麼:)) –

回答

1

我不明白,爲什麼這是一個無限循環,寫控制檯

問題上「給一個intiger」是scanf()不消耗數據,它不能匹配對指定的格式。它使這些字符在輸入流中未讀。因此,如果您嘗試從具有相同格式的同一個流再次讀取,而沒有通過其他方式消耗至少一個字符,則可以確定輸入將再次不匹配。然後再次。然後再次。

爲避免無限循環,每次匹配失敗後,您需要消耗至少一個不匹配輸入字符。有很多方法可以做到這一點;這裏有一個非常簡單的一個:

#include <stdio.h> 

int main() { 
    int a; 

    do { 
     printf("Give an intiger\n"); 
     if (scanf("%d", &a)) { 
      // breaks from the loop on a successful match or an error 
      break; 
     } 
     // consume the remainder of one line of input without storing it 
     if (scanf("%*[^\n]") == EOF) { 
      break; 
     } 
    } while (1); 
} 

消耗上遇到不匹配的輸入線,這將產生一些投入會比許多選擇沒那麼令人驚訝的交互行爲的整個剩餘部分。

如果您已經編寫簡潔的代碼情有獨鍾,或者如果你不喜歡break了一個循環的中間,那麼你可能會這樣寫一樣的東西:

#include <stdio.h> 

int main() { 
    int a; 

    do { 
     printf("Give an intiger\n"); 
    } while ((scanf("%d", &a) == 0) && (scanf("%*[^\n]") != EOF)); 
} 

因爲&&運營商短路,第二個scanf()調用只有在第一個返回零時執行,並且循環將在第一次迭代之後退出,其中第一次scanf()調用返回非零或第二次返回EOF(指示錯誤)。

+0

在scanf後也可以添加getchar()。但爲什麼函數scanf不使用特定格式的數據?這是一個錯誤還是語言決定? –

+0

@DANIROLST,在失敗的'scanf()'後面加上'getchar()'將避免一個*無限*循環,但是它對於某些輸入會產生令人驚訝的行爲。嘗試使用「abc123」。 –

+3

@DANIROLST,C標準明確規定了scanf()的行爲。而且,它是有道理的:如果'scanf()'消耗了不匹配的數據,那麼這些數據就會丟失*,而沒有任何機制讓它恢復。而且,如果'scanf()'想要消耗一些不匹配的輸入,那麼你認爲它應該消耗多少?沒有任何答案適用於任何情況。 –

2

scanf()的問題是,它停止閱讀,當第一對於大多數說明符(如"%d")遇到空白區域,所以它留在輸入中,這就是爲什麼如果您不放棄這樣的空白空間會再次讀取會導致問題,因爲它會在您下次調用它時立即返回。當您按輸入返回'\n'新行字符(或換行)時,會出現一個必填空格。

如果你想讀一個整數,並確保你沒有,你可以嘗試這樣的

#include <stdio.h> 
#include <stdlib.h> 
#include <ctype.h> 

int 
main(void) 
{ 
    long int value; // You can adapt this to use `int' 
        // but be careful with overflow 
    char line[100]; 
    while (fgets(line, sizeof(line), stdin) != NULL) { 
     char *endptr; 
     value = strtol(line, &endptr, 10); 
     if ((isspace(*endptr) != 0) && (endptr != line)) 
      break; 
    } 
    fprintf(stdout, "%ld\n", value); 
    return 0; 
} 

strtol()的說明書,瞭解這段代碼

你可以在評論中所建議的,刪除來自輸入的空白​​字符,但恕我直言,這是很難,你會仍然有其他問題scanf(),如處理空的輸入,這不是直截了當。

+1

說scanf()忽略空格字符,並且在OP的'scanf()'使用*的情況下它不會解釋他描述的問題。 –

+0

@JohnBollinger你是對的!我解決了這個問題,現在我認爲使用OP的代碼更容易理解問題。 –

+0

'(isspace(* endptr)!= 0)'允許輸入'「123 456」'作爲有效。也許'(* endptr ='\ n'|| * endptr ='\ 0')'? – chux