2017-01-29 51 views
2

我希望用戶只輸入兩個整數,但不能超過兩個或少於兩個。另外,在無效輸入時,我希望打印一個錯誤並提示用戶再次輸入兩個整數。用戶應輸入由空格分隔的兩個整數,而不是換行符。因此,例如:
1)的有效輸入是:1 2
2)輸入無效:1
3)無效的輸入:如何防止用戶輸入比C中所需的輸入更多或更少的輸入?

#include<stdio.h> 

int main(){ 

    int first; 
    int second; 
    printf("Enter input:\n"); 
    int returnValue = scanf("%d %d", &first, &second); 
    while(returnValue != 2){ 
     printf("Invalid input. Please enter again: \n"); 
     returnValue = scanf("%d %d", &first, &second); 
    } 
    printf("First: %d Second: %d\n", first, second); 
    return 0; 
} 
:1 2 3

我與以下兩種方法嘗試了

在這個涉及scanf的第一種方法中,我無法阻止用戶在換行符上輸入每個整數。我也不能將輸入限制爲2個數字。也就是說,如果用戶輸入2個以上的整數,則程序接受前2個整數而忽略第3個整數。我想在這種情況下打印錯誤。

我的另一種方法涉及與fgets和sscanf:

#include<stdio.h> 

int main(){ 

    int first; 
    int second; 
    printf("Enter input:\n"); 
    char line[20]; 
    fgets(line, sizeof(line), stdin); 
    int returnValue = sscanf(line, "%d %d", &first, &second); 

    while(returnValue != 2){ 
    printf("Invalid input. Please enter again: \n"); 
    fgets(line, sizeof(line), stdin); 
    returnValue = sscanf(line, "%d %d", &first, &second); 
    } 

    printf("First: %d Second: %d\n", first, second); 
    return 0; 
} 

在這種方法中,我能夠進入,如果只是一個整數後,用戶點擊進入打印錯誤。但我無法將輸入限制爲只有2個數字。也就是說,如果用戶輸入2個以上的整數,則程序接受前2個整數而忽略第3個整數。我想在這種情況下打印錯誤。

所以我的問題是,我的要求是否可以通過修改第一種方法以及第二種方法來實現?

謝謝。

+2

如果沒有硬件支持(例如禁用所有字母的鍵盤,如果用戶碰到字母A時觸電用戶的某種方法等),防止用戶輸入他們選擇的任何輸入是不可能的。你可以做的最多的是接受輸入,並檢查它。如果遇到無效輸入,一些選擇是忽略壞部分,忽略某些數據塊(例如,如果它包含任何錯誤輸入,則爲整行),或者直接丟棄輸入並持續詢問,直到用戶輸入有效的內容。 – Peter

+1

「所以我的問題是,我的要求是否可以通過修改第一種方法以及第二種方法來實現?」 - >是的,[示例](http://stackoverflow.com/a/27805565/2410359),但不值得。使用[@David Bowling](http://stackoverflow.com/a/41917246/2410359) – chux

回答

3

一個解決辦法是,以在兩個%d轉換後使用%n轉換規範。 %n轉換規範不匹配任何字符,但將格式字符串中讀取的字符數存儲到此處。因此,在呼叫:

sscanf(line, "%d %d %n", &first, &second, &bufPos); 

如果第二%d達到,那麼bufPos將在line閱讀最後一個字符之後保持字符的索引。由於在%n之前有一個空格,因此在將索引值存儲在bufPos中之前,將讀取並跳過零個或多個空格字符。因此,在有效輸入之後,bufPos將指示\0終止符。如果在此索引處找到line中的任何其他字符,則輸入中會包含無關字符。

這是您的第二個代碼示例的修改版本。在fgets()讀取一行輸入後,使用sscanf()掃描字符串。如果少於2個匹配,或者line[bufPos]不是'\0',則badInput設置爲true。輸入循環是一個執行一次的do循環,並且只要badInputtrue就繼續執行。

#include <stdio.h> 
#include <stdlib.h>     // for exit() 
#include <stdbool.h>    // for bool type 

#define BUF_SIZE 100 

int main(void) 
{ 
    int first; 
    int second; 
    char line[BUF_SIZE]; 
    int returnValue; 
    int bufPos; 
    bool badInput = false; 

    do { 
     if (badInput) { 
      printf("Invalid input. Please enter again: "); 
      badInput = false; 
     } else { 
      printf("Enter input: "); 
     } 
     if (fgets(line, sizeof(line), stdin) == NULL) { 
      perror("Error in fgets()"); 
      exit(EXIT_FAILURE); 
     } 

     returnValue = sscanf(line, "%d %d %n", &first, &second, &bufPos); 

     if (returnValue < 2 || line[bufPos] != '\0') { 
      badInput = true; 
     } 

    } while (badInput); 

    printf("First: %d Second: %d\n", first, second); 

    return 0; 
} 

樣品的相互作用:從其他答案

Enter input: 1 
Invalid input. Please enter again: 1 2 3 
Invalid input. Please enter again: 
Invalid input. Please enter again: 1 2 
First: 1 Second: 2 
+0

這工作。謝謝! –

1

爲了防止在詢問char *時出現問題,您可以使用regular expression。 如果你沒有被強迫獲得兩個在一個scanf你可以使用此功能:

int secure_input(int max, int min) { 
    int choice,buffer; 
    do { 
     choice = -1;//initialize in a values not included among min and max 
     scanf("%d", &choice); 
     while ((buffer = getchar()) != '\n' ? buffer != EOF : false); // empty the buffer to avoid infinite loop 
    } while (choice > max ? true : choice < min); 
    return choice; 
} 

在你的主要功能,您只需調用這樣的功能:

first = secure_input(2;1); 
+0

在罕見的輸入錯誤上,'choice'的值可能會也可能不會是'-1'。最好測試'scanf()'的返回值。在緩衝區=(char)getchar()中對'char「進行轉換會導致發生文件結束時的無限循環,並且'char'是無符號的。最好使用'int buffer'並放下這個轉換。 – chux

0

不同的是,你還可以使用strtok(),然後檢查多少號碼是如何被發現解析輸入。這種方法很複雜,但它確實提供了對問題的不同看法。

裏面你while()循環,您可以檢查有多少間隔數從fgets()發現,那麼如果僅僅2被發現了,那麼你就可以break圈外。否則,繼續搜索。一旦退出循環,那麼你可以從最近的輸入讀取中得到兩個整數。您也可以使用strtol()來檢查整數是否有效。

注意:strtok()是reeantrant,它會修改它解析的字符串。所以在這種情況下,您可能需要在某處創建它的副本。您可以使用strdup()malloc()來執行此操作。

下面是一些示例代碼,顯示這一點:

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

#define LINESIZE 20 
#define BASE 10 

int main(void) { 
    char line[LINESIZE]; 
    const int n = LINESIZE; 

    char *number, *copy, *endptr; 
    const char *delim = " "; 

    int first, second, check, invalidnum; 
    size_t slen, count; 

    while (1) { 
     printf("Enter input: "); 
     if (fgets(line, n, stdin) == NULL) { 
      printf("Error reading buffer from fgets()\n"); 
      exit(EXIT_FAILURE); 
     } 

     slen = strlen(line); 
     if (slen > 0 && line[slen-1] == '\n') { 
      line[slen-1] = '\0'; 
     } else { 
      printf("Buffer overflow detected\n"); 
      exit(EXIT_FAILURE); 
     } 

     copy = strdup(line); 

     count = 0; 
     invalidnum = 0; 
     number = strtok(copy, delim); 
     while (number != NULL) { 
      check = strtol(number, &endptr, BASE); 
      if (endptr == number || check == 0) { 
       invalidnum = 1; 
       break; 
      } 
      count++; 
      number = strtok(NULL, delim); 
     } 
     free(copy); 
     copy = NULL; 

     if (count != 2 || invalidnum) { 
      printf("Invalid input\n\n"); 
     } else { 
      break; 
     } 
    } 

    if (sscanf(line, "%d %d", &first, &second) != 2) { 
     printf("Unexpected error from sscanf()\n"); 
     exit(EXIT_FAILURE); 
    } 
    printf("first = %d, second = %d\n", first, second); 

    return 0; 
} 

這僅僅是另一種方法,您的問題。就簡單性而言,@David Bowling有更好的想法,我建議使用他的。