我在想程序的问题是什么。 键入quit时无法退出程序。
这是我所拥有的:
#include
#include
int main(void) {
char string[200];
printf("Enter a bunch of words:");
do
{
scanf("%[^
]c", string);
}while(strcmp(string,"quit")!=0);
return 0;
}
scanf(" %199[^
]", string);注意%和199之前的空格,以防止写入string的字符过多。不需要尾随c
您是否对使用scanf进行的锻炼有规范要求?如果不是,则ID建议其他方法。
@GovindParmar是的,我必须使用scanf,我们不能使用我们尚未学习的任何东西。
@UnholySheep不,它不是。它是一种字符串的扫描格式,该字符串包含除换行符之外的任何字符,后跟文字c。
为什么使用scanf("%[^
]c", string);而不是fgets (string, sizeof string, stdin)? (然后是strncmp (string,"quit", 4);)
@ DavidC.Rankin这是学校作业的一部分,我不能使用任何尚未教过的东西,所以我必须使用scanf
那scanf(" %[^
]%*c", string);呢? (*是分配抑制运算符,允许在不影响返回的转换计数的情况下读取和忽略下一个字符(例如%c)。(应检查if (scanf(" %[^
]%*c", string) != 1) { * handle error * })
嗨@anon您应该解释您认为"%[^
]c"的含义。
@AnttiHaapala:并不是真的。 %[^
]将留在输入流中。因此,下一个字符不能为文字c。
@ DavidC.Rankin hmmm,非常感谢您提供此修复程序,非常感谢,但是我仍然没有被教过这种技术,因此我必须以一种更简单的方式来做。有没有办法做while(1)然后在循环内做if(strcmp(string," quit")!= 0)break;香港专业教育学院尝试了这么多的排列,我不知道如何使它工作。
@jxh当然不可能匹配整个格式,但这就是什么意思。
@jxh它在单词之间留有空格。我想,如果我没有它,它将停在第一个空间。
@anon:但是,您想用尾随的c完成什么?
您的两个最大问题是使用scanf困扰新C程序员的两个最常见问题:
You are using an incorrect format string; and
You fail to check the return of scanf.
让我们首先解决第一件事:
scanf("%[^
]c", string);
您的格式字符串"%[^
]c"使用字符类格式说明符"%[...]"来读取string的文本。然后是"c" -仅与输入字符串末尾的文字'c'相匹配。那样写不会发生,因为"%[^
]"将读取不是'
'的所有字符,仅保留'
'才能读取-与'c'不匹配。
此外,"%[...]"说明符与"%c"说明符一起不占用前导空格('
'为空格)。因此,在stdin中保留未读取的'
'时,您对下一个scanf的调用将失败,因为"%[^
]"不会读取'
'并且它与'c'不匹配,从而导致匹配失败,即'
'在stdin中仍然未被读取,事情很快就失控了。
要解决所有问题,您需要记住上面的(2.)并使用字段宽度修饰符来保护string的数组边界,然后您应读取并保存提取并放入string的字符后的字符以验证是否已读取完整的输入行-否则,您有责任在尝试下一次读取之前删除stdin中剩余的所有多余字符。
对于初学者,您可以使用格式适当受限的字符串,该字符串开头应包含space,这将导致scanf放弃所有前导空格,例如
" %199[^
]%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[^
]%c", string, &c);
(注意:提示已在do {...} while (..);循环内移动)
接下来,您将负责每次检查scanf的返回。您必须处理三个条件
(return == EOF)用户通过按Ctrl + d(或在Windows Ctrl + z上)生成手册EOF来取消输入;
(return < expected No. of conversions),必须处理匹配或输入失败,并且必须考虑输入缓冲区中可能剩余的每个字符。 (通常,您将在输入缓冲区中向前扫描,直到找到'
'或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[^
]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)
", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.
", stderr);
empty_stdin();
}
else if (c != '
') { /* check c is '
', else string too long */
fprintf (stderr,"warning: input exceeds %d characters.
",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s
", string);
} while (strcmp (string,"quit") != 0);
最后,请勿在代码中使用幻数(200是幻数)。相反,如果您需要一个常数,则#define一个(或多个)。您必须对数字进行硬编码的唯一地方是例如scanf field-width修饰符-不能使用变量,宏或命名常量。这是该规则的一个例外。同样,请勿对文件名或路径进行硬编码。所有函数都带有参数,甚至main()都将所需的信息传递给程序。
综上所述,您可以执行以下操作:
#include
#include
#define MAXC 200 /* constant - maximum characters in string */
void empty_stdin (void)
{
int c = getchar();
while (c != EOF && c != '
')
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[^
]%c", string, &c);
if (retn == EOF) { /* check the return against EOF */
fputs ("(user canceled input)
", stderr);
return 0;
}
else if (retn < 2) { /* checking both string and c read */
fputs ("input failure.
", stderr);
empty_stdin();
}
else if (c != '
') { /* check c is '
', else string too long */
fprintf (stderr,"warning: input exceeds %d characters.
",
MAXC - 1);
empty_stdin();
}
else /* good input, output string */
printf ("string: %s
", 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
使用Ctrl + d(或在windoze上为Ctrl + z)生成手册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)
将MAXC重置为20并将字段宽度修饰符重置为scanf到19,您可以检查对太长的行的处理,例如第一个输入合适,第二个输入太长:
$ ./bin/scanf_string_quit
Enter a bunch of words ('quit' exits): my dog has fleas and my cat has none.
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): 1234567890123456789
string: 1234567890123456789
Enter a bunch of words ('quit' exits): 12345678901234567890
warning: input exceeds 19 characters.
Enter a bunch of words ('quit' exits): quit
string: quit
仔细检查一下,如果您还有其他问题,请告诉我。
哇!非常感谢,我非常感谢所有细节。 Ive遵循了您和另一个用户的建议,一旦我键入quit,程序就会退出。但是,这导致了一个新问题,因为我没有描述程序的目的,所以你们回答了您认为程序想要的内容,我将发布一个新问题,更全面地解释我拥有程序后想要做什么放弃。
这样的事情可以接受吗?
#include
#include
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识别"退出",或者扫描string的子字符串,因为如果所有内容都会附加在后面,然后您的字符串将是"(...)quit",而strcmp不会将其识别为" quit"。
附带说明一下,请始终初始化数组,否则可能会发生不良情况。
我不能使用memset,我还没在课堂上学到:/。但是,我按照您的建议初始化了数组,并且无需memset即可工作。非常感谢!
@anon您也可以使用for循环将string中的每个索引设置为0,但是任务可能不仅仅需要这样做。很高兴这是有用的。
@Joshua我一直都喜欢Integer Overflow Man,他是我最喜欢的超级英雄。确实存在这种危险,但是匿名人士并没有真正指定他所完成任务的界限,所以我只是想提出最简单的建议,以解决他似乎遇到的问题。内存安全似乎并不是其中的一部分。
鉴于您提供的稀疏解释,最直接的更改可能是修改扫描字符串以吞下而不是理论上的c,这实际上是不可能的:
scanf("%[^
]
", string);
为了防止缓冲区溢出,您应该指定缓冲区必须存储多少空间:
scanf("%199[^
]
", string);
只要您知道输入内容永远不会超过199个字符,这是安全的。但这在理论和实践上都是一个很弱的假设。超过199的字符将在下一次迭代中作为下一个单词被扫描,如果输入的是199 .后跟单词quit,则程序将意外退出。您需要一种更可靠的方法来扫描到该行的其余部分并丢弃它,然后再读取下一行。
您可能会想使用另一个%[...]来捕获额外的字符,如下所示:
scanf("%199[^
]%*[^
]
", string);
但是,对于输入少于或等于199个字符的常见情况,这将失败。这是因为如果转换导致输入为空,则scanf将失败。因此,将保留在输入中。
如果限于使用scanf,则需要将扫描分为多个单独的scanf调用,以便将其余行的错误和非错误扫描视为相同的结果,从而导致第二个scanf吞下换行符本身。
scanf("%199[^
]%*[^
]", string);
scanf("
");