如何防止用户输入超过最大限制的数据?

发布于 2024-10-02 13:20:45 字数 809 浏览 6 评论 0原文

这段代码要求用户提供数据,然后是一个数字:

$ 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 上的所有内容?

This code asks the user for data and subsequently a number:

$ 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);
}
$

The problem is that apart from the instruction that states the maximum number of chars, there is nothing that stops the user from entering more, which is subsequently read into num as junk:

$ ./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
$ 

Is there a way to discard all that is there on STDIN after the fgets call?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

安静被遗忘 2024-10-09 13:20:46

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 */;
}

What "can happen" to fgets?

  1. it returns NULL when there is an error in input
  2. it returns NULL when it finds an EOF before any "real" characters
  3. it returns the pointer to the buffer
    1. the buffer wasn't completely filled
    2. the buffer was completely filled but there is no more data in input
    3. the buffer was completely filled and there is more data in input

How can you distinguish between 1 and 2?
with feof

How can you distinguish between 3.1., 3.2. and 3.3.
By determining where the terminating null byte and line break were written:
If the output buffer has a '\n' then there is no more data (the buffer may have been completely filled)
If there is no '\n' AND the '\0' is at the last position of the buffer, then you know there is more data waiting; if the '\0' is before the last position of the buffer, you've hit EOF in a stream that doesn't end with a line break.

like this

/* 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.)");
    }
}

The usual, incomplete tests, are buf[buflen - 1] == '\n' and checking fgets return value ...

while (fgets(buf, sizeof buf, stdin)) {
    if (buf[strlen(buf) - 1] != '\n') /* deal with extra input */;
}
一城柳絮吹成雪 2024-10-09 13:20:46

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

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 两次并丢弃第一个。

I would read the data and then check it for user error:

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! ");
}

On the other hand, if you don't want to do this, just read num twice and discard the first one.

掐死时间 2024-10-09 13:20:45

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;
}

The scanf() function is terrible for user input, and it's not that great for file input unless you somehow know your input data is correct (don't be that trusting!) Plus, you should always check the return value for fgets() since NULL indicates EOF or some other exception. Keep in mind that you get the user's newline character at the end of your fgets() data unless the maximum is reached first. I might do it this way as a first pass:

#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;
}
唠甜嗑 2024-10-09 13:20:45

据我所知,唯一的便携式解决方案是自己耗尽缓冲区:

while (getchar() != EOF);

请注意 fflush (标准输入);不是答案。

编辑:如果您只想丢弃字符直到下一个换行符,您可以执行以下操作:

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

To my knowledge, the only portable solution is to exhaust the buffer yourself:

while (getchar() != EOF);

Note that fflush(stdin); is not the answer.

EDIT: If you only want to discard characters until the next newline, you can do:

int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文