键入退出以退出C程序

时间:2019-03-06 22:46:39

标签: c string

我想知道程序的问题是什么。键入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;
}

3 个答案:

答案 0 :(得分:4)

您的两个最大问题是使用scanf困扰新C程序员的两个最常见问题:

  
      
  1. 您使用的格式字符串不正确;和
  2.   
  3. 您无法检查scanf的返回。
  4.   

让我们首先解决第一件事:

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); 返回 。您必须处理 三个条件

  1. do {...} while (..);用户通过按 Ctrl + d (或在Windows Ctrl + z 上)生成手册scanf来取消输入; < / li>
  2. (return == EOF),必须处理 matching input 失败,并且必须考虑输入缓冲区中可能剩余的每个字符。 (通常,您将在输入缓冲区中向前扫描,直到发现EOF(return < expected No. of conversions)丢弃所有剩余的多余字符,请参见示例中的'\n'函数);和
  3. 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");