我正在为一个初级C编程课做一个项目,我应该为公司制作一个基本的订购系统。 我的一个函数有问题,它作为一个单独的程序工作正常,但作为订购程序中的一个函数,它不会让我在退出函数之前输入一个新项目。 然而,似乎在获得(项目)之后运行所有内容;因为每次运行它都会添加\ 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输入怪癖”的事实。
我很感谢任何提示和答案,并会在必要时提供更多我的代码。
答案 0 :(得分:5)
这是怎么回事:
<enter>
”。scanf
读取B
个字符。 <enter>
仍在输入流中等待。addItem
被召唤。gets()
被调用,读取仍在等待的<enter>
,并返回一个空行。使用scanf
阅读菜单选择字符后,您可以通过阅读并放弃所有内容(包括下一个换行符)来解决此问题:
int c;
printf("Menu here");
scanf("%c", &menu);
do {
c = getchar();
} while (c != EOF && c != '\n');
答案 1 :(得分:1)
我注意到的一件事。您的do while
循环正在val
检查"X"
,而该值实际上位于menu
。
除了这种可能性(val
可能"X"
开始,这可能导致退出循环而不管输入的值是什么),没有任何事情跳出来显然导致过早退出函数或环。我认为你最好发布完整的代码库,这样我们就不会猜太多了。
更新
不要将以下内容用于家庭作业 - 你几乎肯定会因抄袭而失败(因为你的教育工作者,假设他们不是完全傻瓜,将会留意这些网站的工作)。
我只是想让您了解可以用于用户I / O的内容,以使您的程序更加强大。正如一位有用的灵魂指出的那样,你永远不应该使用没有缓冲区溢出保护的输入例程作为选项,因为这几乎肯定会让恶意输入崩溃你的代码(这是最好的情况,最糟糕的情况是他们将接管你的电脑。)
这意味着没有gets
,您需要使用fgets
,因为它可以限制实际输入的信息量。另外,我倾向于避免使用scanf
和fscanf
,因为这些函数中的任何失败实际上都会将输入文件指针留在不确定的位置。
我发现使用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;
}
答案 2 :(得分:1)
你一次只读一个角色,而scanf()
正在做一些缓冲。如果我输入“ABC”并按Enter键,您的程序将显示为“A”,执行“A”操作,打印“按Enter键继续”,在紧随其后的scanf中读取“B”和“C”。所以当然它会提前回归;你已经给它一些输入,即'B'和'C'。
我建议你使用另一种输入方法。例如,您可以切换到基于行的命令(可能使用fgets()
),这需要您在每一步都按Enter键。或者您可以使用curses
或任何特定于平台的事情来进行非缓冲输入,以便您对按键进行响应而不是stdio
提供的缓冲。