2015-05-17 58 views
1

我想从用户收集数据,我想用fgets()完成任务。避免`fgets()`double输入匹配

在我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); 
} 

一切编译,一切似乎正常工作,但该方案要求时对于用户输入(大部分时间),它要求用户点击输入两次以移动到下一个输入。

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

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

+2

我认为所有'malloc()'调用都是不必要的。简单地读入缓冲区,将它们的长度和它们的指针一起传递。 – trojanfoe

回答

1

具有双输入的问题来自电话的组合,看起来像这样:

get_user_input(some_field); 
flush_stdin(); 
  • 的第一行代码需要用户按为了输入以指示fgets他已完成输入回应。
  • 的第二行代码需要用户按以结束内部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',因为它需要新数据F rom用户,而不必显式刷新输入。

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

2

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

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

这样做的一个好处是,你实际上不需要flush_stdin()函数,因为不会有虚假的新行有时需要丢弃,有时不需要。