我想知道程序的问题是什么。键入quit时无法退出程序。 这是我所拥有的:
#include <stdio.h>
#include <string.h>
int main(void) {
char string[200];
printf("Enter a bunch of words: ");
do
{
scanf("%[^\n]c", string);
}while(strcmp(string,"quit")!=0);
return 0;
}
答案 0 :(得分:4)
您的两个最大问题是使用scanf
困扰新C程序员的两个最常见问题:
- 您使用的格式字符串不正确;和
- 您无法检查
scanf
的返回。
让我们首先解决第一件事:
scanf("%[^\n]c", string);
您的格式字符串"%[^\n]c"
使用字符类格式说明符"%[...]"
来读取string
的文本。然后紧跟"c"
-仅与输入字符串末尾的文字'c'
匹配。由于"%[^\n]"
会读取所有不是'\n'
的字符,而只剩下'\n'
被读取-不匹配 {{ 1}}。
此外,'c'
指示符和"%[...]"
指示符不使用前导空格("%c"
是空格)。因此,在'\n'
中未读'\n'
的情况下,您对stdin
的下一次调用将失败,因为scanf
将不会读取"%[^\n]"
并且与{{1 }}导致匹配失败,'\n'
中的'c'
仍未被读取,事情迅速失控。
要解决所有问题,您需要记住上面的'\n'
,并使用 field-width 修饰符来保护stdin
的数组边界,然后您应该读取并保存并提取以下字符并将其放入(2.)
中,以 validate 读取完整的输入行;如果并非如此, 您的责任 是要删除string
尝试中的字符之前剩余的多余字符下次阅读。
对于初学者,您可以使用受适当限制的格式字符串,该字符串开头应包含string
,这将导致stdin
放弃所有前导空白,例如
space
请注意,上面的最终字符将被保存,将进行 两次 转换,因此您将需要一个字符变量来处理最终转换说明符的结果,例如< / p>
scanf
(注意:提示已在 " %199[^\n]%c"
循环内移动)
下一个 您 负责每次检查 do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
的 返回 。您必须处理 三个条件
do {...} while (..);
用户通过按 Ctrl + d (或在Windows Ctrl + z 上)生成手册scanf
来取消输入; < / li>
(return == EOF)
,必须处理 matching 或 input 失败,并且必须考虑输入缓冲区中可能剩余的每个字符。 (通常,您将在输入缓冲区中向前扫描,直到发现EOF
或(return < expected No. of conversions)
丢弃所有剩余的多余字符,请参见示例中的'\n'
函数);和EOF
表示读取成功-然后由您检查输入是否满足任何其他条件(例如,正整数,正浮点等)。将其完全放在一起,您可以使用empty_stdin()
循环读取并寻找(return == expected No. of conversions)
作为提示退出的关键字,如下所示:
scanf
最后不要在代码中使用魔术数字("quit"
是魔术数字)。相反,如果您需要一个常量,请 do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)\n", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.\n", stderr);
empty_stdin();
}
else if (c != '\n') { /* check c is '\n', else string too long */
fprintf (stderr, "warning: input exceeds %d characters.\n",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s\n", string);
} while (strcmp (string,"quit") != 0);
一个(或多个)。唯一必须硬编码数字的地方是200
field-width 修饰符-不能使用变量,宏或命名常量。这是该规则的一个例外。同样,请勿对文件名或路径进行硬编码。所有函数都带有参数,甚至#define
都将所需的信息传递给程序。
完全将其放入,您可以执行以下操作:
scanf
使用/输出示例
main()
使用 Ctrl + d (或在windoze中为 Ctrl + z )生成手册#include <stdio.h>
#include <string.h>
#define MAXC 200 /* constant - maximum characters in string */
void empty_stdin (void)
{
int c = getchar();
while (c != EOF && c != '\n')
c = getchar();
}
int main (void) {
char string[MAXC]; /* use constants for array bounds */
do {
char c; /* final character read */
int retn; /* variable to save scanf return */
/* prompt */
fputs ("Enter a bunch of words ('quit' exits): ", stdout);
/* read saving scanf return */
retn = scanf (" %199[^\n]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)\n", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.\n", stderr);
empty_stdin();
}
else if (c != '\n') { /* check c is '\n', else string too long */
fprintf (stderr, "warning: input exceeds %d characters.\n",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s\n", string);
} while (strcmp (string,"quit") != 0);
return 0;
}
:
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): My dog has fleas and my cat has none.
string: My dog has fleas and my cat has none.
Enter a bunch of words ('quit' exits): quit
string: quit
将EOF
重置为$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): Hello
string: Hello
Enter a bunch of words ('quit' exits): (user canceled input)
,将 field width 修饰符重置为MAXC
重置为20
,您可以检查对太长的行的处理,例如第一个输入合适,第二个输入太长:
scanf
仔细检查一下,如果还有其他问题,请告诉我。
答案 1 :(得分:1)
这样的东西可以接受吗?
#include <stdio.h>
#include <string.h>
int main(void) {
char string[200] = {0};
printf("Enter a bunch of words: ");
do {
memset(string, 0, 200);
scanf("%s", string);
} while (strcmp(string, "quit") != 0);
return 0;
}
您还没有确切说明您打算使用字符串做什么,因此很难给出答案。但是,要注意的一件事是,您必须对string
做一些操作(我刚刚在这里将其归零),以便strcmp
能够识别“退出”,或者扫描{{ 1}},因为如果所有内容都始终附加,那么您的字符串将是“(...)quit”,而strcmp不会将其识别为“ quit”。
请注意,请务必初始化数组,否则可能会发生不良情况。
答案 2 :(得分:0)
鉴于您提供的稀疏解释,最直接的更改可能是修改扫描字符串以吞下\n
而不是理论上的c
,但这实际上是不可能的:
scanf("%[^\n]\n", string);
为防止缓冲区溢出,应指定缓冲区必须存储多少空间:
scanf("%199[^\n]\n", string);
这是安全的,只要您知道输入永远不会超过199个字符即可。但这在理论和实践上都是一个很弱的假设。保存的199以上的字符将作为下一个单词的下一个迭代扫描,如果输入的199 .
后跟单词quit
,则程序将意外退出。您需要一种更可靠的方法来扫描到该行的其余部分,并在读取下一行之前将其丢弃。
您可能会想使用另一个%[...]
来捕获这样的多余字符:
scanf("%199[^\n]%*[^\n]\n", string);
但是,在输入少于或等于199个字符的常见情况下,这将失败。这是因为如果转换导致输入为空,scanf
将失败。因此,\n
将保留在输入中。
如果限于使用scanf
,则需要将扫描分为多个单独的scanf
调用,以便将其余行的错误和非错误扫描视为相同的结果,导致第二个scanf
吞下换行符本身。
scanf("%199[^\n]%*[^\n]", string);
scanf("\n");