避免`fgets()`重复输入匹配 [英] Avoiding `fgets()` double Enter hits

查看:57
本文介绍了避免`fgets()`重复输入匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to collect data from user and I want to accomplish the task using 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');

以下是我为完成工作而编写的功能:

Here are the functions I have written to get the job done:

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 进入下一个输入.

Everything compiles and everything seems to work properly, but when the program asks for user input, (most of the times) it requires the user to hit Enter twice to move on to the next input.

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

On one hand I know that this behaviour is caused by the flush_stdin() function. On the other I can't get rid of the flush_stdin() function because it makes sure that no input field gets skipped. If I did the program would output something like First Name: Last Name:

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

How do I avoid the double hit and make sure all input is collected?

推荐答案

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

The problem with double-enter comes from combinations of calls that looks like this:

get_user_input(some_field);
flush_stdin();

  • 代码的第一行要求用户按 Enter 以便向 fgets 表示他已完成响应的键入.
  • 第二行代码要求用户按 Enter ,以结束 flush_stdin
  • 中的 while 循环.

    • The first line of code requires the user to press Enter in order to indicate to fgets that he has finished typing the response.
    • The second line of code requires the user to press Enter in order to end the while loop inside flush_stdin
    • 要解决此问题,您应该删除对 flush_stdin 的调用,并修改 get_user_input 以跳过空行:

      To fix this problem you should remove calls to flush_stdin, and modify get_user_input to skip over empty lines:

      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',因为它会从用户那里获取新数据,而不会您必须显式刷新输入.

      The do/while loop will skip over unexpected '\n' in the input as it takes new data from the user, without you having to flush the input explicitly.

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

      Note that you can remove malloc/free, along with string copying, from your get_user_input, because the caller already provides a sufficient buffer. Your code assumes that the buffer is at least STRLEN long, but it may be better to pass the length explicitly as a second parameter to the function.

      这篇关于避免`fgets()`重复输入匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆