字符串比预期的要长,并被视为多个输入
String is longer than expected, and is treated as multiple inputs
这是我第一次 post 来这里,我对 C 语言还比较陌生(这只是我在 uni 上的第二个单元)。
基本上,我正在尝试编写一段代码,询问用户是否希望继续。如果他们写 "yes",代码将继续循环。如果他们写否,代码将终止程序。如果他们写了其他任何东西,它只会再次询问,直到有可识别的输入。我正在使用 scanf 和 printf,并尝试仅使用它们来创建此代码。
char userInput[4];
userInput[0] = '[=10=]';
while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
{
printf("Do you want to continue (yes/no) :");
scanf("%3s", userInput);
}
为了简单起见,我没有包含其余代码。
比如我的输入是
xxx
输出是
Do you want to continue (yes/no) :
很好。但是,如果我输入:
xxxx
输出为:
Do you want to continue (yes/no) :Do you want to continue (yes/no) :
如果我输入
xxxxxxx
我明白了
Do you want to continue (yes/no) :Do you want to continue (yes/no) :Do you want to continue (yes/no) :
看起来它几乎是在预期长度之后保存其余字符,并立即将它们发送到输入或其他什么?我想针对太长的字符串建立保护,但我认为这并不理想。
如果问题结构不当,我很抱歉,欢迎任何建设性的批评。我在任何地方都找不到这个确切的问题,所以我想问问自己。
欢迎使用 scanf
进行用户输入的陷阱。 fgets
或 POSIX getline
是更好的选择,因为它们避免了 scanf
固有的许多 ("What's left in the input buffer?") 问题。也就是说,知道如何正确使用 scanf
很重要。
使用scanf
无非就是计算(1)实际读取了哪些字符? (2) 输入缓冲区中哪些字符未读?为了处理第 2 个问题,您需要一些方法来清空在调用 scanf
之间输入缓冲区中剩余的所有字符,以确保您不会被剩余的任何字符咬住。您还必须知道至少要保留一个字符,否则您将只是阻塞等待输入被清除。清空 stdin
的标准方法是循环遍历剩余的任何字符,直到您读取结尾的 "\n'
(用户按下 "Enter" 的结果)或 EOF
。例如,
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(您也可以将其写成单个 for
语句,例如 for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}
,但 while 通常更具可读性。)
不只是正确处理输入的问题。由于要强制用户输入"yes"或"no",一般的做法是不断循环,直到满足输入条件。你在正确的轨道上,但没有完全到达那里。相反,你可以这样做:
char buffer[1024] = ""; /* don't skimp on buffer size */
for (;;) { /* loop continually */
int rtn; /* value to hold return from scanf */
printf ("Do you want to continue (yes/no): "); /* prompt */
rtn = scanf ("%1023[^\n]", buffer); /* read input */
if (rtn == EOF) { /* user canceled input, bail */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (rtn == 1) { /* value stored in buffer */
/* check for input meeting criteria */
if (strcmp (buffer, "yes") == 0 || strcmp (buffer, "no") == 0) {
empty_stdin(); /* don't leave characters in stdin */
break; /* success, break input loop */
}
}
/* handle wrong input */
empty_stdin(); /* don't leave characters in stdin */
fprintf (stderr, "error: invalid input.\n\n");
}
如果您以这种方式进行读取,您可以控制输入缓冲区中保留的内容,不仅针对循环中的每次读取,而且可以确保在您离开时用户输入的缓冲区中没有字符。
如上所述,正确使用 scanf 只是一个计算问题,一旦您知道每个 格式说明符 的行为方式,以及 input or matching failures 在失败点停止读取,你可以回答以下问题(1)实际读取了哪些字符?和 (2) 输入缓冲区中哪些字符未读?
一个简短的例子可能会有所帮助:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void) {
char buffer[1024] = ""; /* don't skimp on buffer size */
for (;;) { /* process loop - doing stuff */
printf ("\nprogram processing....\n\n");
for (;;) { /* input - loop continually */
int rtn; /* value to hold return from scanf */
printf ("Do you want to continue (yes/no): "); /* prompt */
rtn = scanf ("%1023[^\n]", buffer); /* read input */
if (rtn == EOF) { /* user canceled input, bail */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (rtn == 1) { /* value stored in buffer */
/* check for input meeting criteria */
if (strcmp (buffer, "yes") == 0) {
empty_stdin(); /* don't leave characters in stdin */
break; /* success, break input loop */
}
else if (strcmp (buffer, "no") == 0)
goto alldone;
}
/* handle wrong input */
empty_stdin(); /* don't leave characters in stdin */
fprintf (stderr, "error: invalid input.\n\n");
}
}
alldone:;
printf ("that's all folks...\n");
}
(注意: 以上 "yes"
和 "no"
响应分别进行比较,以允许在 "yes"
或 [=29 上继续处理=] 在 "no"
)
例子Use/Output
$ ./bin/scanf_yes_no
program processing....
Do you want to continue (yes/no): what?
error: invalid input.
Do you want to continue (yes/no): obnoxious long line because cat stepped on kbd
error: invalid input.
Do you want to continue (yes/no): yes
program processing....
Do you want to continue (yes/no): yes
program processing....
Do you want to continue (yes/no):
error: invalid input.
Do you want to continue (yes/no): no
that's all folks...
当您使用 scanf("%3s", userInput)
时,它将只读取 3
以 '[=13=]'
结尾的字符到 userInput
缓冲区。但是,如果您输入的字符多于 3
个字符,其余字符仍存在于输入缓冲区中等待 scanf
读取。
您可以在每次 scanf
之后清空缓冲区以避免这种意外。
#include <stdio.h>
#include <string.h>
int main(void)
{
char userInput[4];
userInput[0] = '[=10=]';
int c;
while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
{
printf("Do you want to continue (yes/no) :");
scanf("%3s", userInput);
while(1) // drain the input
{
c = getchar ();
if(c=='\n') break;
if(c==EOF) return -1;
}
}
return 0;
}
It seems like it's almost saving the rest of the characters after the expected length, and sending them to the input straight away or something?
是的,这正是发生的事情。
那些字符就在那里,坐在输入缓冲区中,等待被消耗,就像前三个字符在某个时间点一样。
您通过操作系统将字符从键盘发送到终端。根据您编写的内容,您的程序将接受文本直到宇宙尽头(尽管一次三个字符)。只要有更多的可用字符,它就会吃掉它们,而不管您的击键在时间上分布多远都没有关系。 C++ 无法知道您希望它执行任何其他操作。
也就是说,通过调整您的语义(如其他答案所示),对于像这样的 call/response 提示,可以获得更直观的行为。
这是我第一次 post 来这里,我对 C 语言还比较陌生(这只是我在 uni 上的第二个单元)。
基本上,我正在尝试编写一段代码,询问用户是否希望继续。如果他们写 "yes",代码将继续循环。如果他们写否,代码将终止程序。如果他们写了其他任何东西,它只会再次询问,直到有可识别的输入。我正在使用 scanf 和 printf,并尝试仅使用它们来创建此代码。
char userInput[4];
userInput[0] = '[=10=]';
while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
{
printf("Do you want to continue (yes/no) :");
scanf("%3s", userInput);
}
为了简单起见,我没有包含其余代码。
比如我的输入是
xxx
输出是
Do you want to continue (yes/no) :
很好。但是,如果我输入:
xxxx
输出为:
Do you want to continue (yes/no) :Do you want to continue (yes/no) :
如果我输入
xxxxxxx
我明白了
Do you want to continue (yes/no) :Do you want to continue (yes/no) :Do you want to continue (yes/no) :
看起来它几乎是在预期长度之后保存其余字符,并立即将它们发送到输入或其他什么?我想针对太长的字符串建立保护,但我认为这并不理想。
如果问题结构不当,我很抱歉,欢迎任何建设性的批评。我在任何地方都找不到这个确切的问题,所以我想问问自己。
欢迎使用 scanf
进行用户输入的陷阱。 fgets
或 POSIX getline
是更好的选择,因为它们避免了 scanf
固有的许多 ("What's left in the input buffer?") 问题。也就是说,知道如何正确使用 scanf
很重要。
使用scanf
无非就是计算(1)实际读取了哪些字符? (2) 输入缓冲区中哪些字符未读?为了处理第 2 个问题,您需要一些方法来清空在调用 scanf
之间输入缓冲区中剩余的所有字符,以确保您不会被剩余的任何字符咬住。您还必须知道至少要保留一个字符,否则您将只是阻塞等待输入被清除。清空 stdin
的标准方法是循环遍历剩余的任何字符,直到您读取结尾的 "\n'
(用户按下 "Enter" 的结果)或 EOF
。例如,
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(您也可以将其写成单个 for
语句,例如 for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}
,但 while 通常更具可读性。)
不只是正确处理输入的问题。由于要强制用户输入"yes"或"no",一般的做法是不断循环,直到满足输入条件。你在正确的轨道上,但没有完全到达那里。相反,你可以这样做:
char buffer[1024] = ""; /* don't skimp on buffer size */
for (;;) { /* loop continually */
int rtn; /* value to hold return from scanf */
printf ("Do you want to continue (yes/no): "); /* prompt */
rtn = scanf ("%1023[^\n]", buffer); /* read input */
if (rtn == EOF) { /* user canceled input, bail */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (rtn == 1) { /* value stored in buffer */
/* check for input meeting criteria */
if (strcmp (buffer, "yes") == 0 || strcmp (buffer, "no") == 0) {
empty_stdin(); /* don't leave characters in stdin */
break; /* success, break input loop */
}
}
/* handle wrong input */
empty_stdin(); /* don't leave characters in stdin */
fprintf (stderr, "error: invalid input.\n\n");
}
如果您以这种方式进行读取,您可以控制输入缓冲区中保留的内容,不仅针对循环中的每次读取,而且可以确保在您离开时用户输入的缓冲区中没有字符。
如上所述,正确使用 scanf 只是一个计算问题,一旦您知道每个 格式说明符 的行为方式,以及 input or matching failures 在失败点停止读取,你可以回答以下问题(1)实际读取了哪些字符?和 (2) 输入缓冲区中哪些字符未读?
一个简短的例子可能会有所帮助:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void) {
char buffer[1024] = ""; /* don't skimp on buffer size */
for (;;) { /* process loop - doing stuff */
printf ("\nprogram processing....\n\n");
for (;;) { /* input - loop continually */
int rtn; /* value to hold return from scanf */
printf ("Do you want to continue (yes/no): "); /* prompt */
rtn = scanf ("%1023[^\n]", buffer); /* read input */
if (rtn == EOF) { /* user canceled input, bail */
fprintf (stderr, "user canceled input.\n");
exit (EXIT_FAILURE);
}
else if (rtn == 1) { /* value stored in buffer */
/* check for input meeting criteria */
if (strcmp (buffer, "yes") == 0) {
empty_stdin(); /* don't leave characters in stdin */
break; /* success, break input loop */
}
else if (strcmp (buffer, "no") == 0)
goto alldone;
}
/* handle wrong input */
empty_stdin(); /* don't leave characters in stdin */
fprintf (stderr, "error: invalid input.\n\n");
}
}
alldone:;
printf ("that's all folks...\n");
}
(注意: 以上 "yes"
和 "no"
响应分别进行比较,以允许在 "yes"
或 [=29 上继续处理=] 在 "no"
)
例子Use/Output
$ ./bin/scanf_yes_no
program processing....
Do you want to continue (yes/no): what?
error: invalid input.
Do you want to continue (yes/no): obnoxious long line because cat stepped on kbd
error: invalid input.
Do you want to continue (yes/no): yes
program processing....
Do you want to continue (yes/no): yes
program processing....
Do you want to continue (yes/no):
error: invalid input.
Do you want to continue (yes/no): no
that's all folks...
当您使用 scanf("%3s", userInput)
时,它将只读取 3
以 '[=13=]'
结尾的字符到 userInput
缓冲区。但是,如果您输入的字符多于 3
个字符,其余字符仍存在于输入缓冲区中等待 scanf
读取。
您可以在每次 scanf
之后清空缓冲区以避免这种意外。
#include <stdio.h>
#include <string.h>
int main(void)
{
char userInput[4];
userInput[0] = '[=10=]';
int c;
while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
{
printf("Do you want to continue (yes/no) :");
scanf("%3s", userInput);
while(1) // drain the input
{
c = getchar ();
if(c=='\n') break;
if(c==EOF) return -1;
}
}
return 0;
}
It seems like it's almost saving the rest of the characters after the expected length, and sending them to the input straight away or something?
是的,这正是发生的事情。
那些字符就在那里,坐在输入缓冲区中,等待被消耗,就像前三个字符在某个时间点一样。
您通过操作系统将字符从键盘发送到终端。根据您编写的内容,您的程序将接受文本直到宇宙尽头(尽管一次三个字符)。只要有更多的可用字符,它就会吃掉它们,而不管您的击键在时间上分布多远都没有关系。 C++ 无法知道您希望它执行任何其他操作。
也就是说,通过调整您的语义(如其他答案所示),对于像这样的 call/response 提示,可以获得更直观的行为。