【问题标题】:Function that prompts user for integer value and checks for valid input提示用户输入整数值并检查有效输入的函数
【发布时间】:2019-03-20 16:05:18
【问题描述】:

我目前被困在我需要完成的任务的一小部分。 作业的一项要求是

“调用一个函数,提示用户输入二次方程的系数 a、b 和 c 的每个值,并返回输入的值,并检查有效输入的错误(scanf 返回一个值)。”

我不知道该怎么做。我可以很容易地提示用户输入,我可以检查它是否是有效的输入,但我不知道如何把它变成一个函数。我当前的代码是:

   {
   if (isalpha(a))
   {
      printf("INPUT ERROR!\n");
      printf("Enter a value for a: ");
      scanf("%d", &a);
   }
   } //this is how I would normally check the input

   int main(void) //start of main() function definition
   {
      int a, b, c, n, D; //declares integer variables a, b, c, n, and D
      float root1, root2; //declares float variables root1 and root2

      do //do while loop starts here
      {
         printf("Enter a value for a: "); //prompts user to input integer for variable 'a'
         scanf("%d", &a); //reads an integer from the keyboard and stores in the variable 'a' 
         printf("%d\n", a); //returns value of integer that was input for variable 'a'

         printf("Enter a value for b: "); //prompts user to input integer for variable 'b'
         scanf("%d", &b); //reads an integer from the keyboard and stores in the variable 'b'
         printf("%d\n", b); //returns value of integer that was input for variable 'b'

         printf("Enter a value for c: "); //prompts user to input integer for variable 'c'
         scanf("%d", &c); //reads an integer from the keyboard and stores in the variable 'c'
         printf("%d\n", c); //returns value of integer that was input for variable 'c'
         ...}

对于任何格式错误,我深表歉意,但这基本上是我坚持使用的程序的一部分。

我的问题是,我怎样才能将第一个函数与 do/while 循环中的所有内容结合起来,以创建一个可以调用 3 次的大函数?

我不知道如何使用函数将 a 的所有实例切换为 b 和 c,因为我以前从未真正使用过这样的函数。

【问题讨论】:

标签: c function methods do-while


【解决方案1】:

提示用户输入整数值并检查有效输入的功能

如果用户只逐行输入有效的整数文本,那么代码很简单:

// Overly idealized case
fputs(prompt, stdout);
char buf[50];
fgets(buf, sizeof buf, stdin);
int i = atoi(buf);

但是用户是good, bad and ugly 并且**它发生了。如果代码想要读取一行,将其解析为范围内的int,并检测一系列问题,下面是审查许多典型的虚假和恶意输入问题的代码。

我对检测过长的输入特别感兴趣,因为它是恶意的,并且作为一种针对黑客的谨慎设计而无效。如下所示,几乎没有理由允许对超过 20 个字符的 32 位 int 进行有效输入。这种理性值得更深入的解释。

  • 文件结束
  • 输入流错误
  • 溢出
  • 没有领先的数字测试
  • 尾随非数字文本
  • 过长的行

首先使用fgets() 读取 输入,然后应用各种int 验证测试。如果fgets() 没有读取整行,则读取其余部分。

#include <limits.h>
#include <ctype.h>

// Max number of `char` to print an `int` is about log10(int_bit_width)
// https://stackoverflow.com/a/44028031/2410359
#define LOG10_2_N 28
#define LOG10_2_D 93
#define INT_DEC_TEXT (1 /*sign*/ + (CHAR_BIT*sizeof(int)-1)*LOG10_2_N/LOG10_2_D + 1)

// Read a line and parse an integer
// Return:
//   1: Success
//   0: Failure
//   EOF: End-of-file or stream input error
int my_get_int(int *i) {
  // Make room for twice the expected need. This allows for some
  // leading/trailing spaces, leading zeros, etc.
  //           int        \n  \0
  char buf[(INT_DEC_TEXT + 1 + 1) * 2];
  if (fgets(buf, sizeof buf, stdin) == NULL) {
    *i = 0;
    return EOF; // Input is either at end-of-file or a rare input error.
  }
  int success = 1;

  char *endptr;
  errno = 0;
  long value = strtol(buf, &endptr, 10);


  // When `int` is narrower than `long`, add these tests
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (value < INT_MIN) {
    value = INT_MIN;
    errno = ERANGE;
  } else if (value > INT_MAX) {
    value = INT_MAX;
    errno = ERANGE;
  }
#endif
  *i = (int) value;
  if (errno == ERANGE) {
    success = 0;  // Overflow
  }

  if (buf == endptr) {
    success = 0; // No conversion
  }

  // Tolerate trailing white-space
  // Proper use of `is...()` obliges a `char` get converted to `unsigned char`.
  while (isspace((unsigned char ) *endptr)) {
    endptr++;
  }

  // Check for trailing non-white-space
  if (*endptr) {
    success = 0; // Extra junk
    while (*endptr) {  // quietly get rest of buffer
      endptr++;
    }
  }

  // Was the entire line read?
  // Was the null character at the buffer end and the prior wasn't \n?
  const size_t last_index = sizeof buf / sizeof buf[0] - 1;
  if (endptr == &buf[last_index] && buf[last_index - 1] != '\n') {
    // Input is hostile as it is excessively long.
    success = 0;  // Line too long
    // Consume text up to end-of-line
    int ch;
    while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
      ;
    }
  }

  return success;
}

示例用法

     puts("Enter a value for a: ", stdout);
     fflush(stdout);  // Insure output is seen before input.
     int a;
     if (my_get_int(&a) == 1) {
       printf("a:%d\n", a); 
     }

【讨论】:

    【解决方案2】:

    我的问题是,我怎样才能将第一个函数与 do/while 循环中的所有内容结合起来,以创建一个可以调用 3 次的大函数?

    嗯,功能不必很大。要考虑的因素是提示字符串和要读取的变量 - 后者可以留在调用 main() 并从返回值分配。关于如何通常会检查输入,我建议将此检查留给scanf() 并仅测试其返回值。

    #include <stdio.h>
    #include <stdlib.h>
    
    int input(char *prompt)
    {   // prompts user to input integer
        // reads an integer from standard input and returns it
        int a, s;   // leave it to scanf to check the input:
        while (printf("%s", prompt), fflush(stdout), s = scanf("%d", &a), !s)
        {
            printf("INPUT ERROR!\n");
            do s = getchar(); while (s != '\n' && s != EOF);    // consume bad input
        }
        if (s == EOF) puts(""), exit(0);    // no more input
        return a;
    }
    

    main() 中你可以这样做

         a = input("Enter a value for a: ");
         b = input("Enter a value for b: ");
         c = input("Enter a value for c: ");
    

    (没有循环)。

    【讨论】:

    • 相当不错input(char *prompt)。虽然它不能很好地处理 line 输入,但它可以很好地处理 stream 输入。
    • @chux - 您是否愿意详细说明它如何处理输入
    • 1) scanf("%d", &amp;a) 首先使用前导空格,包括 '\n'。当输入为"\n" 时,该无编号行不提示错误。 2) 当输入为“123 45x\n”时,input() 返回 123,将“45x\n”的剩余留在stdin。 3)注意超出范围的输入,如"12345678901234567890" 使用scanf("%d"... 调用UB
    【解决方案3】:

    scanf() 已经根据格式说明符 (%d) 为您处理输入,因此您只需要了解 scanf 的工作原理并使用它来检查和构建您的函数:)

    当您编写scanf("%d", &amp;a); 时,由于%d 说明符,程序希望您写入一个整数,如果读取一个整数,程序会将其写入变量a

    但是函数scanf也有一个返回值,也就是说,你可以做check = scanf("%d", &amp;a);check在这种情况下会有一个0或1的值。这是因为返回值记录了成功读取了多少个值。如果您输入dsfugnodg,则没有数字,因此它将返回0。如果您输入659 32,它将成功读取第一个值并返回1。

    你的函数看起来像:

    #include <stdio.h>
    
    int getAndPrint(char label)
    {
        int n = 0, val = 0;
        do {
            printf("Enter a value for %c: ", label);
            n = scanf("%d", &val);
            if (n == 0) {
                printf("Error, invalid value entered.\n");
                /* Consume whatever character leads the wrong input 
                 * to prevent an infinite loop. See: 
                 * https://stackoverflow.com/questions/1669821/scanf-skips-every-other-while-loop-in-c */
                getchar();
            }
        } while (n == 0);
        printf("%c = %d\n", label, val);
        return val;
    }
    
    int main()
    {
        int a, b, c;
        a = getAndPrint('a');
        b = getAndPrint('b');
        c = getAndPrint('c');
        printf("a=%d, b=%d, c=%d\n", a, b, c);
    }
    

    另请参阅: Scanf skips every other while loop in C

    【讨论】:

    • @chux 修复了它。 rmgk 不客气,欢迎来到 StackExchange。多逛一逛,你会发现 cmets 是为了澄清问答等,而不是为了感谢:)
    • @chux 现在怎么样了?
    • @chux 哇,有两件事我没有看到: 1. 将返回的字符保存到 c 是不必要的,可以直接完成 getchar() != \n; 2. 我不必关心我要删除的内容,因为如果任何数字跟随错误的输入,我们无论如何都不会在代码中的那个位置,并且它会被正确读取。谢谢你的耐心,我学到了很多。
    【解决方案4】:

    我认为您想要以下代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>  // for isalpha
    
    void InputAndCheck(int * pValue, const char * pName)
    {
        do
        {
            printf("Enter a value for %s: ", pName);
            scanf("%d", pValue);
            if (isalpha(*pValue))
            {
                printf("INPUT ERROR!\n");
                continue;
            }
            else
            {
                break;
            }
        } while (1);
    
        // clear the input buffer
        fflush(stdin);
        return;
    }
    
    int main()
    {
        int a, b, c;
        InputAndCheck(&a, "a");
        InputAndCheck(&b, "b");
        InputAndCheck(&c, "c");
        printf("a=%d;b=%d;c=%d;\r\n",a,b,c);
        return 0;
    }
    

    【讨论】:

    【解决方案5】:

    您正在寻找的是对函数的介绍。 这是一个:https://www.tutorialspoint.com/cprogramming/c_functions.htm

    这是编程中非常重要的组成部分,您绝对应该学习掌握这个概念。

    函数将允许您在不同的上下文中反复执行一些代码,只需更改上下文(参数)。

    是这样声明的

    int add(int first, int second){
        //here we can use first and second
        printf("first: %d\n", first);
        printf("second: %d\n", second);
    
        //and eventually return a value
        return first+second;
    
    }
    

    现在在使用时,我们正在重用之前的代码来执行任务,结果将根据我们传递的参数而有所不同。

    printf("1+2 = %d\n", add(1, 2));
    -->3
    printf("2+2 = %d\n", add(2, 2));
    -->4
    

    您的任务的示例解决方案:

    //this will handle validation
    int validateInput(int input){
        if(isalpha(input)){
            printf("INPUT ERROR!\n");
            return 0;
        }
        return 1;
    }
    
    //this will prompt the user and return input only if the input is valid
    int askForCoefficient(unsigned char coefficientName){
        int valid = 0;
        int value = 0;
        while(!valid){
            printf("Enter a value for %c: ", coefficientName);
            value = scanf("%d", &value);
            valid = validateInput(value);
    
        }
    
        printf("%d\n", value);
    
        return value;
    
    }
    

    【讨论】:

    • value = scanf("%d", &amp;value); valid = validateInput(value); 是奇怪的代码。该代码将 0、1 或 EOF 传递给 validateInput(value),它们都不会是 alpha
    猜你喜欢
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-28
    相关资源
    最近更新 更多