【问题标题】:C Function exiting before inputC函数在输入前退出
【发布时间】:2010-12-22 15:10:04
【问题描述】:

我目前正在为初学者 C 编程课程做一个项目,我应该为公司制作一个基本的订购系统。 我的一个函数有问题,它作为一个单独的程序运行良好,但作为订购程序中的一个函数,它不会让我在退出函数之前输入一个新项目。 然而,它似乎贯穿了gets(item);之后的所有内容;因为我每次运行它都会得到添加的 \n。

这是我的代码:

do{
printf("Menu here");
scanf("%c", &menu);
switch(menu)
{
    case 'A':
        listItem();
        break;

    case 'B':
        addItem();
        break;

    ...

    case 'X':
        break;
}

printf("Press Enter to continue.");
scanf("%c%c", &enter, &enter);
system("cls");

}while(menu != 'X');


void addItem()
{
    char item[30];
    printf("\nAdd new item: ");
    gets(item);
    FILE * output;
    output = fopen("items.txt", "a");
    fputs(item, output);
    fprintf(output, "\n");
    fclose(output);
}

转换后的内容是我的老师认为的一种丑陋但有效的方法,可以解决我们在本课程中没有深入研究他所说的“C 输入的怪癖”这一事实。

感谢任何提示和答案,如有必要,我会提供更多代码。

【问题讨论】:

    标签: c


    【解决方案1】:

    发生了什么是这样的:

    1. 程序打印菜单。
      • 用户输入“B<enter>”。
      • scanf 读取 B 字符。 <enter> 仍在输入流中等待。
      • addItem 被调用。
      • 调用gets(),读取仍在等待的<enter>,并返回一个空行。

    您可以通过在使用scanf 读取菜单选择字符后读取并丢弃直到下一个换行符的所有内容来修复它:

    int c;
    
    printf("Menu here");
    scanf("%c", &menu);
    do {
        c = getchar();
    } while (c != EOF && c != '\n');
    

    【讨论】:

      【解决方案2】:

      您一次读取一个字符,scanf() 正在做一些缓冲。如果我键入“ABC”并按 Enter,您的程序将读取“A”,执行“A”的操作,打印“按 Enter 继续”,在紧随其后的 scanf 中读取“B”和“C”。所以它当然会早点回来;你已经给了它一些输入,即'B'和'C'。

      我建议您使用另一种获取输入的方法。例如,您可以切换到基于行的命令(可能使用fgets()),这要求您在每一步都按回车键。或者你可以使用curses 或任何你需要做非缓冲输入的平台特定的东西,这样你就可以对按键做出反应,而不是stdio 提供的缓冲。

      【讨论】:

        【解决方案3】:

        我立即注意到了一件事情。您的do while 循环正在检查val 中的"X",而该值实际上在menu 中。

        除了这种可能性(val 可能是 "X" 开头,这可能导致退出循环,无论输入的值如何),没有任何东西跳出显然会导致函数或循环过早退出。我认为你最好发布你的完整代码库,这样我们就不用猜测太多了。

        更新:

        不要在作业中使用以下内容 - 你几乎肯定会因为剽窃而失败(因为你的教育工作者,假设他们不是完全的傻瓜,会寻找从这些网站上获取的工作)。

        我只是想让您了解可以使用哪些用户 I/O 来使您的程序更加健壮。正如一位乐于助人的人指出的那样,您永远不应该使用没有缓冲区溢出保护的输入例程作为选项,因为这几乎肯定会允许恶意输入使您的代码崩溃(这是最好的情况,最坏的情况是它们将接管您的计算机)。

        这意味着没有gets,您需要改用fgets,因为它可以限制实际输入的信息量。此外,我倾向于避免使用scanffscanf,因为这些函数中的任何故障实际上都会使输入文件指针处于不确定的位置。

        我发现使用fgets 获得整行要好得多,检查您是否确实获得整行,然后在该行上使用sscanf。这样一来,您就可以确定您在一条线的边界上,您已经拥有一整条线,然后您可以sscanf 那条线随心所欲,直到您将它与某些东西相匹配。

        为此,您可能需要查看以下代码:

        #include <stdio.h>
        
        #define FSPEC "file.txt"
        
        // Skip to the end of the line. This is used in some
        // places to ensure there's no characters left in the
        // input buffer. It basically discards characters
        // from that buffer until it reaches the end of a line.
        
        static void skipLine (void) {
            char ch = ' ';
            while ((ch != '\n') && (ch != EOF))
                ch = getchar();
        }
        

         

        // Get a line of input from the user (with length checking).
        
        static char *getLine (char *prompt, char *line, int sz) {
            // Output prompt, get line if available.
            // If no line available (EOF/error), output newline.
        
            printf ("%s", prompt);
            if (fgets (line, sz, stdin) == NULL) {
                printf ("\n");
                return NULL;
            }
        
            // If line was too long (no '\n' at end), throw away
            // rest of line and flag error.
        
            if (line[strlen (line) - 1] != '\n') {
                skipLine();
                return NULL;
            }
        
            // Otherwise line was complete, return it.
        
            return line;
        }
        

         

        // Output the menu and get a choice from the user.
        
        static char doMenu (void) {
            char cmd[1+2]; // need space for char, '\n' and '\0'.
        
            // Output the menu.
        
            printf ("\n");
        
            printf ("\n");
            printf ("Main menu\n");
            printf ("---------\n");
            printf ("1. Input a line\n");
            printf ("2. Output the file\n");
            printf ("3. Clear the file\n");
            printf ("\n");
            printf ("x. Exit\n");
            printf ("\n");
        
            // Get the user input and return it.
        
            if (getLine ("Enter choice (1,2,3,x): ", cmd, sizeof(cmd)) == NULL)
                return '\n';
        
            printf ("\n");
        
            return cmd[0];
        }
        

         

        static void doOption1 (void) {
            FILE *fh;
            char *ln;
            char buff[15+2]; // need space for line, '\n' and '\0'.
        
            // Get and check line, add to file if okay.
        
            if ((ln = getLine ("Enter line: ", buff, sizeof(buff))) == NULL) {
                printf ("Bad input line\n");
            } else {
                fh = fopen (FSPEC, "a");
                if (fh != NULL) {
                    fputs (ln, fh);
                    fclose (fh);
                }
            }
        }
        

         

        static void doOption2 (void) {
            FILE *fh;
            int intch;
        
            // Output the file contents.
        
            printf ("=====\n");
            fh = fopen (FSPEC, "r");
            if (fh != NULL) {
                while ((intch = fgetc (fh)) != EOF)
                    putchar (intch);
                fclose (fh);
            }
            printf ("=====\n");
        }
        

         

        static void doOption3 (void) {
            FILE *fh;
        
            // Clear the file.
        
            fh = fopen (FSPEC, "w");
            if (fh != NULL)
                fclose (fh);
        }
        

         

        // Main program basically just keeps asking the user for input
        // until they indicate they're finished.
        
        int main (void) {
            char menuItem;
        
            // Get asking for user input until exit is chosen.
        
            while ((menuItem = doMenu()) != 'x') {
                switch (menuItem) {
                    case '1': doOption1(); break;
                    case '2': doOption2(); break;
                    case '3': doOption3(); break;
                    default:  printf ("Invalid choice\n"); break;
                }
            }
        
            return 0;
        }
        

        【讨论】:

        • break 只会跳出 switch 语句而不是 while 循环。
        • @Lucas,您的陈述是正确的,但我无法找到我实际提出该论点的位置。我说过如果 val 设置为“X”,则循环将退出,而没有关于退出循环的 break 语句。
        • 菜单和 val 问题是我把我的变量翻译成英文搞砸了,我错过了将最后一个 val 更改为菜单,循环条件在我的原始代码中有效
        • @paxdiablo:我相信 Lucas 指的是使用 while(1) 循环。一开始我也这么认为,永远循环是更糟糕的选择,直到我想到了切换后的处理。由于菜单循环不在main() 中,因此永远循环和用于case 'X':return 应该可以很好地工作。
        • @paxdiablo:我误解了你的第一句话。它读取它,因为您暗示 switch-break 中的“中断”将打破 while 循环。
        猜你喜欢
        • 1970-01-01
        • 2011-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-20
        • 2021-02-21
        相关资源
        最近更新 更多