【问题标题】:Avoiding `fgets()` double Enter hits避免`fgets()`双输入命中
【发布时间】:2015-05-17 10:43:09
【问题描述】:

我正在尝试从用户那里收集数据,我想使用 fgets() 完成任务。

在我的main.c 文件中:

do
    {
        user = ask_user_info();

        // ... Code exporting data to file ...

        free(user);

        fprintf(stdout, "Do you want to add another user?\nChoice: ");
        scanf("%c[^\n]", &choice);

    } while (choice == 'y');

这是我为完成工作而编写的函数:

UserData *ask_user_info()
{
    char firstname[STRLEN];
    char lastname[STRLEN];
    char username[STRLEN];
    char password[STRLEN];
    char email[STRLEN];

    fprintf(stdout, "First Name: ");
    get_user_input(firstname);
    flush_stdin();

    fprintf(stdout, "Last Name: ");
    get_user_input(lastname);
    flush_stdin();

    fprintf(stdout, "Username: ");
    get_user_input(username);
    flush_stdin();

    fprintf(stdout, "Password: ");
    get_user_input(password);
    flush_stdin();

    fprintf(stdout, "Email: ");
    get_user_input(email);
    flush_stdin();

    return fill_fields(firstname, lastname, username, password, email);
}

void get_user_input(char *input)
{
    int length;
    char *buffer = (char *) malloc (STRLEN * sizeof(char));
    (*buffer) = '\0';

    if (fgets(buffer, STRLEN, stdin) != NULL)
    {
        length = strlen(buffer)-1;
        buffer[length] = '\0';
        strncpy(input, buffer, length+1);
    }

    free(buffer);
}

UserData *fill_fields(const char firstname[], const char lastname[], 
                        const char username[], const char password[], const char email[])
{
    UserData *user = (UserData *) malloc (sizeof(UserData));

    user->firstname = (char *) malloc (strlen(firstname) * sizeof(char));
    strncpy(user->firstname, firstname, strlen(firstname));
    user->lastname = (char *) malloc (strlen(lastname) * sizeof(char));
    strncpy(user->lastname, lastname, strlen(lastname));
    user->username = (char *) malloc (strlen(username) * sizeof(char));
    strncpy(user->username, username, strlen(username));
    user->password = (char *) malloc (strlen(password) * sizeof(char));
    strncpy(user->password, password, strlen(password));
    user->email = (char *) malloc (strlen(email) * sizeof(char));
    strncpy(user->email, email, strlen(email));

    return user;
}

void flush_stdin()
{
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
}

一切都编译好了,一切似乎都正常工作,但是当程序要求用户输入时,(大多数时候)它需要用户按两次 Enter 才能继续下一个输入。

一方面我知道这种行为是由flush_stdin() 函数引起的。另一方面,我无法摆脱flush_stdin() 函数,因为它确保不会跳过任何输入字段。如果我这样做,程序会输出类似First Name: Last Name:

如何避免双重打击并确保收集所有输入?

【问题讨论】:

  • 我认为所有那些malloc() 电话都是不必要的。只需读入缓冲区,将它们的长度连同它们的指针一起传递。

标签: c fgets


【解决方案1】:

真正的诀窍是不要将fgets()scanf() 和相关函数混合使用。原因是它们处理换行符的方式不同。 fgets() 如果缓冲区足够长以容纳完整的行,则将其读入。 scanf(),取决于格式字符串的选择,在换行符处停止并将其留在流中 - 它将导致 fgets() 的下一次调用立即返回。

尝试使用fgets() 来处理所有来自用户的阅读。如果需要,您可以使用sscanf() 来解释用户输入的字符串。

这样做的一个好处是您实际上不需要flush_stdin() 函数,因为不会出现有时需要丢弃、有时不需要丢弃换行符的虚假情况。

【讨论】:

    【解决方案2】:

    双重输入的问题来自如下所示的调用组合:

    get_user_input(some_field);
    flush_stdin();
    
    • 第一行代码要求用户按 Enter 以向fgets 表明他已完成输入响应。
    • 第二行代码要求用户按Enter 以结束flush_stdin 内的while 循环

    要解决此问题,您应该删除对flush_stdin 的调用,并修改get_user_input 以跳过空行:

    void get_user_input(char *input) {
        int length;
        *input = '\0';
        do {
            if (fgets(buffer, STRLEN, stdin) == NULL) {
                break;
            }
            length = strlen(buffer)-1;
            buffer[length] = '\0';
        } while (length == 0);
    }
    

    do/while 循环将跳过输入中意外的'\n',因为它从用户那里获取新数据,而无需显式刷新输入。

    请注意,您可以从get_user_input 中删除malloc/free 以及字符串复制,因为调用者已经提供了足够的缓冲区。您的代码假定缓冲区至少为 STRLEN 长,但最好将长度作为第二个参数显式传递给函数。

    【讨论】:

      猜你喜欢
      • 2019-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-07
      相关资源
      最近更新 更多