【问题标题】:How to prevent the user from entering more data than the maximum limit?如何防止用户输入超过最大限制的数据?
【发布时间】:2011-05-09 20:12:58
【问题描述】:

此代码向用户询问数据和随后的数字:

$ cat read.c
#include<stdio.h>
#include<stdlib.h>
#define MAX 10

int main() {
    char* c = (char*) malloc(MAX * sizeof(char));
    int num;

    printf("Enter data (max: %d chars):\n", MAX);
    fgets(c, MAX, stdin);
    // how do I discard all that is there on STDIN here?

    printf("Enter num:\n");
    scanf("%d", &num);

    printf("data: %s", c);
    printf("num: %d\n", num);
}
$

问题在于,除了说明最大字符数的指令外,没有什么可以阻止用户输入更多字符,随后将其作为垃圾读入num

$ ./read
Enter data (max 10 chars):
lazer
Enter num:
5
data: lazer
num: 5
$ ./read
Enter data (max 10 chars):
lazerprofile
Enter num:
data: lazerprofnum: 134514043
$ 

有没有办法在fgets 调用之后丢弃STDIN 上的所有内容?

【问题讨论】:

    标签: c stdin flush fgets


    【解决方案1】:

    据我所知,唯一可移植的解决方案是自己耗尽缓冲区:

    while (getchar() != EOF);
    

    请注意,fflush(stdin);not the answer

    编辑:如果您只想丢弃字符直到下一个换行符,您可以这样做:

    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF);
    

    【讨论】:

    • 非常喜欢你的回答。但是我遇到了一个问题,即 fgets 要求我在非常大的字符串上按两次输入。它只是挂在那里等待另一个进入新闻。任何想法为什么?再次感谢
    【解决方案2】:

    fgets“会发生什么”?

    1. 输入错误时返回NULL
    2. 当它在任何“真实”字符之前找到 EOF 时,它会返回 NULL
    3. 它返回指向缓冲区的指针
      1. 缓冲区未完全填满
      2. 缓冲区已完全填满,但输入中没有更多数据
      3. 缓冲区已完全填满,输入中有更多数据

    您如何区分12
    feof

    你如何区分3.1.3.2.3.3.
    通过确定终止空字节和换行符的写入位置:
    如果输出缓冲区有'\n',则没有更多数据(缓冲区可能已完全填满)
    如果没有'\n' AND '\0' 在缓冲区的最后一个位置,那么你知道还有更多的数据在等待;如果'\0' 在缓冲区的最后一个位置之前,则您在不以换行符结尾的流中点击了EOF

    喜欢这个

    /* fgets fun */
    /*
    char buf[SOMEVALUE_LARGERTHAN_1];
    size_t buflen;
    */
    if (fgets(buf, sizeof buf, stdin)) {
        buflen = strlen(buf);
        if (buflen) {
            if (buf[buflen - 1] == '\n') {
                puts("no more data (3.1. or 3.2.)"); /* normal situation */
            } else {
                if (buflen + 1 == sizeof buf) {
                    puts("more data waiting (3.3.)"); /* long input line */
                } else {
                    puts("EOF reached before line break (3.1.)"); /* shouldn't happen */
                }
            }
        } else {
            puts("EOF reached before line break (3.1.)"); /* shouldn't happen */
        }
    } else {
        if (feof(stdin)) {
            puts("EOF reached (2.)"); /* normal situation */
        } else {
            puts("error in input (1.)");
        }
    }
    

    通常的、不完整的测试是buf[buflen - 1] == '\n' 和检查fgets 返回值...

    while (fgets(buf, sizeof buf, stdin)) {
        if (buf[strlen(buf) - 1] != '\n') /* deal with extra input */;
    }
    

    【讨论】:

      【解决方案3】:

      scanf() 函数对于用户输入来说很糟糕,而且它对于文件输入也不是很好,除非你知道你的输入数据是正确的(不要那么信任!)另外,你应该总是检查返回值fgets() 因为 NULL 表示 EOF 或其他一些异常。请记住,除非首先达到最大值,否则您会在 fgets() 数据的末尾获得用户的换行符。作为第一遍,我可能会这样做:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #define MAX 10
      
      void eat_extra(void) {
          int ch;
      
          // Eat characters until we get the newline
          while ((ch = getchar()) != '\n') {
              if (ch < 0)
                  exit(EXIT_FAILURE); // EOF!
          }
      }
      
      int main() {
          char c[MAX+1]; // The +1 is for the null terminator
          char n[16]; // Arbitrary maximum number length is 15 plus null terminator
          int num;
      
          printf("Enter data (max: %d chars):\n", MAX);
          if (fgets(c, MAX, stdin)) { // Only proceed if we actually got input
              // Did we get the newline?
              if (NULL == strchr(c, '\n'))
                  eat_extra(); // You could just exit with "Too much data!" here too
      
              printf("Enter num:\n");
              if (fgets(n, sizeof(n) - 1, stdin)) {
                  num = atoi(n); // You could also use sscanf() here
                  printf("data: %s", c);
                  printf("num: %d\n", num);
              }
          }
      
          return 0;
      }
      

      【讨论】:

      • 它应该是fgets(c, MAX+1, stdin) 而不是MAX。和fgets(n, sizeof(n), stdin) 而不是sizeof(n) - 1。阅读fgets的手册
      • "如果读取了任何字符并且没​​有错误,则会附加一个 `\0' 字符来结束字符串。"这就是为什么我总是使用最大长度减一。
      【解决方案4】:

      我会读取数据,然后检查它是否存在用户错误:

      bool var = true;
      while var {
      printf("Enter data (max: %d chars):\n", MAX);
      fgets(c, MAX, stdin);
      // how do I discard all that is there on STDIN here?
      if(strlen(c) <= 10)
      var = false;
      else
      printf("Too long, try again! ");
      }
      

      另一方面,如果您不想这样做,只需读取 num 两次并丢弃第一个。

      【讨论】:

      • -1 在fgets(c, MAX, stdin); 之后,strlen(c)总是小于 MAX。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 2019-05-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多