2015-09-23 99 views
2
#include <stdio.h> 
#include <stdlib.h> 
#include <ctype.h> 
#include <math.h> 

int main(int argc, char * argv[]) 
{ 
    printf("This program tests your integer arithmetic skills.\n" 
      "You should answer the questions following the same \n" 
      "rules that computers do for integers arithmetic, not \n" 
      "floating-point arithmetic. Hit the 'Enter' key after \n" 
      "you have typed in your input. When you wish to finish \n" 
      "the test, enter -9876 as the answer to a question.\n" 
      "\n"); 

    int n1, n2, answer, user_answer, a, b, int_per; 
    char op, c; 
    float per, count, count_r, count_w; 

    count = 0; 
    count_r = 0; 
    count_w = 0; 

    printf("What is your question? "); 
    scanf("%d %c %d", &n1, &op, &n2); 


    do 
    { 
     count++; 

     printf("What is %d %c %d ? ", n1, op, n2); 

     if (op == '+') 
     { 
     answer = n1 + n2; 
     } 
     else if (op == '-') 
     { 
     answer = n1 - n2; 
     } 
     else if (op == '%') 
     { 
     answer = n1 % n2; 
     } 
     else if (op == '/') 
     { 
     answer = n1/n2; 
     } 
     else if (op == '*') 
     { 
     answer = n1 * n2; 
     } 

     c = scanf("%d", &user_answer); 

     if (user_answer == answer) 
     { 
     printf("Correct!\n\n"); 
     count_r++; 

     } 
     else if (user_answer == -9876) 
     { 
     count = count - 1; 
     break; 
     } 
     else if (c != 1) 
     { 
     printf("Invalid input, it must be just a number\n\n"); 
     printf("What is %d %c %d ? ", n1, op, n2); 
     } 
     else if (user_answer != answer) 
     { 
     printf("Wrong!\n\n"); 
     count_w++; 
     } 

    } while(user_answer != -9876); 

    per = (count_r/count) * 100; 

    a = (int) count_r; 
    b = (int) count_w; 
    int_per = roundf(per); 

    printf("\nYou got %d right and %d wrong, for a score of %d%c\n", a, 
      b, int_per, 37); 

    return EXIT_SUCCESS; 

} 

上面的代碼應該循環詢問問題,然後答案,直到用戶輸入-9876作爲答案然後程序終止和給他們他們的分數。這一切都行得通!一件事。當用戶在輸入中輸入一個非數字時。發生這種情況時,應該說「輸入無效,請重試」,然後再次提出同樣的問題。例如雖然循環卡住了無限循環,我不知道爲什麼

你的問題是什麼? 9 + 9

什麼是9 + 9? 嗯,8

輸入無效,請重試

什麼是9 + 9?

SO ..用戶輸入「hmmm」,而不是再次提示相同問題的用戶,然後正確掃描它只是跳入一個無限循環。我想知道如何解決這個問題。

感謝

+0

無效輸入時清除輸入緩衝區。 – BLUEPIXY

+1

當你使用'scanf(「%d」,&value)掃描一個整數'並且輸入不是一個有效整數時,'scanf'返回0並且輸入返回到它開始的位置,即在非數字輸入。隨後的調用將嘗試重新解析相同的無效輸入。你應該'scanf(「%* s」)'跳過它。更好的方法是,用'fgets'和'sscanf'將整行讀入它們中。 –

+0

我不明白爲什麼會使用輸入'-9876'? – ameyCU

回答

2

在呼叫

c = scanf("%d", &user_answer); 

%d轉換說明符期望看到一串十進制數字字符;它會告訴scanf跳過任何前導空格,然後讀取十進制數字字符到第一個非十進制數字字符,然後轉換並將結果保存到user_answer。如果您在 鍵入輸入scanf將讀取並消耗'1''2'字,值12分配給user_answer並返回1(對於一個成功的轉換和分配)離開'a'和換行符輸入流中的字符。

當你鍵入"hmmm",第一個非空白字符不是十進制數字,所以scanf離開它在的地方,不分配什麼user_answer,並返回0。所有其他調用scanf"%d"轉換說明符將做同樣的事情。

所以,你需要確保你的scanf是成功的,如果沒有,清除所有字符從輸入流中做另一個讀,像下面前:

if ((c = scanf("%d", &user_answer)) == 0) 
{ 
    /** 
    * input matching failure, clear stray characters from input stream 
    * up to the next newline character 
    */ 
    while (getchar() != '\n') 
    ; // empty loop 
} 
else if (c == EOF) 
{ 
    // error occurred during input operation 
} 
else 
{ 
    // do something with user_answer 
} 

你會發現在我的第一個例子,%d轉換說明符接受輸入"12a";它將12轉換並分配給user_answer,在輸入流中保留'a'字符以使下一次讀取變糟。理想情況下,您希望完全拒絕這樣糟糕的輸入。你可以做類似如下:

/** 
* Repeatedly prompt and read input until we get a valid decimal string 
*/ 
for(;;) 
{ 
    int c, dummy; 
    printf("What is %d %c %d ? ", n1, op, n2); 

    if ((c = scanf("%d%c", &user_answer, &dummy) == 2) 
    { 
    /** 
    * If the character immediately following our numeric input is 
    * whitespace, then we have a good input, break out of the read loop 
    */ 
    if (isspace(dummy)) 
     break; 
    else 
    { 
     fprintf(stderr, "Non-numeric character follows input, try again...\n"); 
     while (getchar() != '\n') 
     ; // empty loop body 
    } 
    } 
    else if (c == 1) 
    { 
    /** 
    * No character following successful decimal input, meaning we 
    * hit an EOF condition before any trailing characters were seen. 
    * We'll consider this a good input for our purposes and break 
    * out of the read loop. 
    */ 
    break; 
    } 
    else if (c == 0) 
    { 
    /** 
    * User typed in one or more non-digit characters; reject the input 
    * and clear out the input stream 
    */ 
    fprintf(stderr, "Non-numeric input\n"); 

    /** 
    * Consume characters from the input stream until we see a newline. 
    */ 
    while ((getchar() != '\n') 
     ; // empty loop body 
    } 
    else 
    { 
    /** 
    * Input error or EOF on read; we'll treat this as a fatal 
    * error and bail out completely. 
    */ 
    fprintf(stderr, "Error occurred during read, panicking...\n"); 
    exit(0); 
    } 
} 

另一種選擇是讀你的輸入文字,然後用strtol庫函數將其轉換爲結果類型:

for (;;) 
{ 
    char input[SIZE]; // for some reasonable SIZE value, at least 12 to handle 
        // a 32-bit int (up to 10 digits plus sign plus 
        // string terminator 

    printf("What is %d %c %d ? ", n1, op, n2); 
    if (fgets(input, sizeof input, stdin)) 
    { 
    char *check; // will point to first non-digit character in input buffer 
    int tmp = (int) strtol(input, &check, 10); 
    if (isspace(*check) || *check == 0) 
    { 
     user_answer = tmp; 
     break; 
    } 
    else 
    { 
     fprintf(stderr, "%s is not a valid input, try again\n", input); 
    } 
    } 
    else 
    { 
    /** 
    * error or EOF on input, treat this as a fatal error and bail 
    */ 
    fprintf(stderr, "EOF or error while reading input, exiting...\n"); 
    exit(0); 
    } 
} 

這是我的首選方法。

無論是那些混亂的一個將取代線

c = scanf("%d", &user_answer); 

閱讀所有你可能在想,「用C交互式輸入肯定是一個痛苦的屁股」後。你說得對。

1

當在讀取輸入的錯誤,你應該清除錯誤狀態,並嘗試讀取更多的用戶輸入之前跳過行的其餘部分。

更換線:

else if (c != 1) 
    { 
    printf("Invalid input, it must be just a number\n\n"); 
    printf("What is %d %c %d ? ", n1, op, n2); 
    } 

通過

else if (c != 1) 
    { 
    printf("Invalid input, it must be just a number\n\n"); 

    // Clear the error states. 
    clearerr(stdin); 

    // Skip the rest of the line 
    skipRestOfLine(stdin); 

    printf("What is %d %c %d ? ", n1, op, n2); 
    } 

其中skipRestOfLine()可以被實現爲:

void skipRestOfLine(FILE* stream) 
{ 
    int c; 
    while ((c = fgetc(stream)) != '\n' && c != EOF); 
} 
+0

爲什麼'fgetc()'而不是'getc()'? –

+0

@JonathanLeffler,我不明白爲什麼一個人會比另一個更好。 –

+0

'getc()'可以是一個宏; 'fgetc()'保證是一個函數。如果'getc()'是一個宏,那是因爲它可以執行得更好。在某種程度上,這可以追溯到內聯函數之前的日子。這不是一個大問題,但使用'getchar()'和'getc()'通常比'fgetc()'更好。 –

-1
scanf("%d%c%d",&n1, &op, &n2) use this code 
+0

這並沒有解決OP的問題,並引入了一個新的問題。在第一個'%d'和'%c'轉換說明符之間至少需要有一個空格,否則'op'會在第一個輸入後緊接着第一個字符,無論是運算符還是空白符。 –