分词输入中的无限循环
Infinite Loop in Tokenizing Input
大家下午好,我正在为虚拟 shell 程序在 C 中对输入进行标记化,但我遇到了无限循环问题。在我的示例代码中,workwithtokenstuff()
正在无限执行。我以前形成过类似的循环,我不确定为什么会跳过底部的 scanf。示例输入是“cd /dummydir”。我有另一个 strtok()
调用将目录拆分为另一个字符数组。
char inStr[255];
char *token;
scanf("%[^\n\r]", inStr);
token = strtok(inStr, " ");
while (strcmp(token, "exitcom") != 0) {
workwithtokenstuff();
scanf("%[^\n\r]", inStr);
token = strtok(inStr, " ");
}
第二个 scanf
并没有真正被跳过,它捕获前一个 scanf
留在输入缓冲区中的换行符,说明符前的 space 是通常的修复,它消耗缓冲区中存在的白色space 个字符。
char inStr[255];
char *token;
scanf("%254[^\n\r]", inStr); // note the width limit, avoids buffer overflow
token = strtok(inStr, " ");
while (strcmp(token, "exitcom") != 0)
{
workwithtokenstuff(); // assuming this does not consume any more tokens
scanf(" %254[^\n\r]", inStr);
// ^ space here
token = strtok(inStr, " ");
}
脚注:
为了获得更健壮的代码,建议检查 scanf
的 return 值。
也许您已经意识到这一点,但我还是要提一下 strtok
,除此之外,它还更改了原始字符串。如果您需要更多详细信息,请查看 How does the strtok function in C work?.
不管您对读取的数据做了什么,如果您在两次 scanf
调用之间不使用标准输入中的任何数据,那么是的,预计第二个不会使用或转换任何额外的输入。那是因为在第一个之后...
scanf("%[^\n\r]", inStr);
...,如果标准输入中还有可用的字符,那么下一个字符要么是换行符,要么是回车符 return(当然,假设行为最终没有被定义由于超出 inStr
的范围)。与大多数 scanf
字段指令不同,%[
不会跳过前导白色 space,因此如果此时您执行 ...
scanf("%[^\n\r]", inStr);
... 再次遇到的第一个字符(如果有的话)是相同的回车 return 或换行符,(再次)从扫描集中排除。 scanf
调用因此终止而不会消耗或转换任何字符。它将 return 0 或 EOF
取决于实际上是否有任何字符可供阅读。
抛开缓冲区溢出的严重风险,您需要在两次 scanf
调用之间至少消耗一个字符,以便让第二次和后续调用有机会读取任何内容,否则插入一个前导 space 字符进入您的 scanf
格式,以跳过 %[
不会自动执行的前导白色 space。此外,您需要检查 every scanf
调用的 return 值,以确定它是否成功转换了任何数据,以及尝试消费是否有任何意义通过后续调用获得更多。
大家下午好,我正在为虚拟 shell 程序在 C 中对输入进行标记化,但我遇到了无限循环问题。在我的示例代码中,workwithtokenstuff()
正在无限执行。我以前形成过类似的循环,我不确定为什么会跳过底部的 scanf。示例输入是“cd /dummydir”。我有另一个 strtok()
调用将目录拆分为另一个字符数组。
char inStr[255];
char *token;
scanf("%[^\n\r]", inStr);
token = strtok(inStr, " ");
while (strcmp(token, "exitcom") != 0) {
workwithtokenstuff();
scanf("%[^\n\r]", inStr);
token = strtok(inStr, " ");
}
第二个 scanf
并没有真正被跳过,它捕获前一个 scanf
留在输入缓冲区中的换行符,说明符前的 space 是通常的修复,它消耗缓冲区中存在的白色space 个字符。
char inStr[255];
char *token;
scanf("%254[^\n\r]", inStr); // note the width limit, avoids buffer overflow
token = strtok(inStr, " ");
while (strcmp(token, "exitcom") != 0)
{
workwithtokenstuff(); // assuming this does not consume any more tokens
scanf(" %254[^\n\r]", inStr);
// ^ space here
token = strtok(inStr, " ");
}
脚注:
为了获得更健壮的代码,建议检查
scanf
的 return 值。也许您已经意识到这一点,但我还是要提一下
strtok
,除此之外,它还更改了原始字符串。如果您需要更多详细信息,请查看 How does the strtok function in C work?.
不管您对读取的数据做了什么,如果您在两次 scanf
调用之间不使用标准输入中的任何数据,那么是的,预计第二个不会使用或转换任何额外的输入。那是因为在第一个之后...
scanf("%[^\n\r]", inStr);
...,如果标准输入中还有可用的字符,那么下一个字符要么是换行符,要么是回车符 return(当然,假设行为最终没有被定义由于超出 inStr
的范围)。与大多数 scanf
字段指令不同,%[
不会跳过前导白色 space,因此如果此时您执行 ...
scanf("%[^\n\r]", inStr);
... 再次遇到的第一个字符(如果有的话)是相同的回车 return 或换行符,(再次)从扫描集中排除。 scanf
调用因此终止而不会消耗或转换任何字符。它将 return 0 或 EOF
取决于实际上是否有任何字符可供阅读。
抛开缓冲区溢出的严重风险,您需要在两次 scanf
调用之间至少消耗一个字符,以便让第二次和后续调用有机会读取任何内容,否则插入一个前导 space 字符进入您的 scanf
格式,以跳过 %[
不会自动执行的前导白色 space。此外,您需要检查 every scanf
调用的 return 值,以确定它是否成功转换了任何数据,以及尝试消费是否有任何意义通过后续调用获得更多。