避免 `fgets()` 两次输入命中
Avoiding `fgets()` double Enter hits
我正在尝试从用户那里收集数据,我想使用 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) = '[=11=]';
if (fgets(buffer, STRLEN, stdin) != NULL)
{
length = strlen(buffer)-1;
buffer[length] = '[=11=]';
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:
的内容
如何避免重复命中并确保收集到所有输入?
真正的诀窍是不要混合使用 fgets()
与 scanf()
和相关函数。原因是它们处理换行符的方式不同。 fgets()
如果缓冲区足够长以容纳完整行,则将其读入。 scanf()
,根据格式字符串的选择,在换行符处停止并将其留在流中 - 它将立即导致下一次调用 fgets()
到 return。
尝试使用 fgets()
来处理来自用户的 all 阅读。如果需要,您可以使用 sscanf()
来解释用户输入的字符串。
这样做的一个好处是您实际上不需要 flush_stdin()
函数,因为不会出现有时需要丢弃有时不需要的换行符的虚假情况。
双重输入的问题来自如下所示的调用组合:
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 = '[=11=]';
do {
if (fgets(buffer, STRLEN, stdin) == NULL) {
break;
}
length = strlen(buffer)-1;
buffer[length] = '[=11=]';
} while (length == 0);
}
do
/while
循环将跳过输入中意外的 '\n'
,因为它从用户那里获取新数据,而无需显式刷新输入。
请注意,您可以从 get_user_input
中删除 malloc
/free
以及字符串复制,因为调用方已经提供了足够的缓冲区。您的代码假定缓冲区至少 STRLEN
长,但最好将长度作为第二个参数显式传递给函数。
我正在尝试从用户那里收集数据,我想使用 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) = '[=11=]';
if (fgets(buffer, STRLEN, stdin) != NULL)
{
length = strlen(buffer)-1;
buffer[length] = '[=11=]';
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:
如何避免重复命中并确保收集到所有输入?
真正的诀窍是不要混合使用 fgets()
与 scanf()
和相关函数。原因是它们处理换行符的方式不同。 fgets()
如果缓冲区足够长以容纳完整行,则将其读入。 scanf()
,根据格式字符串的选择,在换行符处停止并将其留在流中 - 它将立即导致下一次调用 fgets()
到 return。
尝试使用 fgets()
来处理来自用户的 all 阅读。如果需要,您可以使用 sscanf()
来解释用户输入的字符串。
这样做的一个好处是您实际上不需要 flush_stdin()
函数,因为不会出现有时需要丢弃有时不需要的换行符的虚假情况。
双重输入的问题来自如下所示的调用组合:
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 = '[=11=]';
do {
if (fgets(buffer, STRLEN, stdin) == NULL) {
break;
}
length = strlen(buffer)-1;
buffer[length] = '[=11=]';
} while (length == 0);
}
do
/while
循环将跳过输入中意外的 '\n'
,因为它从用户那里获取新数据,而无需显式刷新输入。
请注意,您可以从 get_user_input
中删除 malloc
/free
以及字符串复制,因为调用方已经提供了足够的缓冲区。您的代码假定缓冲区至少 STRLEN
长,但最好将长度作为第二个参数显式传递给函数。