8.7.1 任务
我们来更具体的考虑菜单程序需要执行的任务。该程序需要获取用户的响应,并且需要基于该响应选择一系列的动作。而且,程序还应该提供一种方法让用户可以回到菜单以做更多的选择。C的switch语句是筛选动作的一个很方便的工具,因为每个用户选择可对应于一个特定的case标签。可以使用while语句来提供对菜单的重复访问。可以使用伪代码按照下列方式描述该过程:
get choice
while choice is not 'q'
switch to desired chice and execute it
get next choice
8.7.2 使执行更顺利
程序顺利执行的目标:处理正确输入时顺利执行和处理错误输入时顺利执行。
例如,您能做的一件事是让“获取选项”部分筛选掉不合适的响应,从而仅使正确的响应被传送到switch语句。这表明须为输入过程提供一个只返回正确响应的函数。将其与while循环、switch语句相结合会产生下列的程序结构:
#include <stdio.h>
char get_choice (void);
void count(void);
int main(void)
{
int choice ;
while((choice=get_choice())!='q')
{
switch(choice)
{
case 'a' : printf("Buy low ,sell high.\n");
break;
case 'b' : putchar('\a'); /*ANSI*/
break;
case 'c' : count();
break;
default : printf("program error!\n");
break;
}
}
return 0;
}
定义get_choice()函数使其只返回值‘a' 'b' 'c'和'q'。我们令实际的菜单选项十分简单,以使您把精力集中在程序结构上;很快我们会讨论count()函数。default语句是方便调试的。如果get_choice函数没能将其返回值限制为预期值,则default语句可以使您了解发生了一些可疑的事情。
get_choice()函数
下面的伪代码是这个函数的一个可能的设计:
show choice
get response
while response is not acceptable
prompt for more response
get response
下面是一个虽然简单但使用起来不太方便的实现:
char get_choice (void)
{
int ch;
printf("Enter the letter of your choice:\n");
printf("a.advice b.bell \n");
printf("c.count d.quit\n");
ch=getchar();
while ((ch<'a' || ch>'c') && ch !='q')
{
printf("Please respond with a,b,c or q.\n");
ch=getchar();
}
return ch;
}
问题在于,使用缓冲输入时,函数会将回车键产生的每个换行符都作为一个错误的响应对待。要使程序界面更加顺畅,该函数应该跳过换行符。
要实现这一目标为多种方法。一种方法是用一个名为get_first()的新函数代替getchar();该函数读取一行的第一个字符并将其余字符丢弃掉。此方法还有一个优点,就是将由act组成的输入行看作是输入了一个简单的a,而不是将其作为由代表count的c所产生的一个有效的响应。记住这一目标,您就可以将输入函数重写为如下的形式:
char get_choice (void)
{
int ch;
printf("Enter the letter of your choice:\n");
printf("a.advice b.bell \n");
printf("c.count d.quit\n");
ch=get_first(); /*get_first()函数*/
while ((ch<'a' || ch>'c') && ch !='q')
{
printf("Please respond with a,b,c or q.\n");
ch=get_first(); /*get_first()函数*/
}
return ch;
}
char get_first(void)
{
int ch;
ch=getchar(); /*读取下一个字符*/
while(getchar()!='\n')
continue; /*跳过本行的剩余部分*/
return ch;
}
8.7.3 混合字符和数字输入
创建菜单提供了将字符输入与数值输入相混合会产生问题的另一个实例。例如,假设count()函数(选项C)如下所示:
void count (void)
{
int n,i;
printf("Count how far?Enter an integer: \n");
scanf("%d",&n);
for (i=1;i<=n;i++)
printf("%d\n",i);
}
如果您通过输入3进行响应,则scanf()将读取3并留下一个换行符,把它作为输入队列中的下一个字符。对get_choice()的下一次调用会导致get_first()返回此换行符,从而导致不希望的动作。
解决该问题的一种方法是重写get_first()使其返回下一个非空白字符,而不是简单的返回它遇到的下一个字符 。第二种方法是由count()函数自己来负责处理换行符。
void count(void)
{
int n,i;
printf("Count how far?Enter an integer: \n");
n=get_int();
for(i=1;i<=n;i++)
printf("%d",i);
while(getchar()!='\n')
continue;
}
此函数使用了程序清单8.7中的get_int()函数;回忆一下,该函数检查有效输入并给用户重新尝试的机会。
程序清单8.8 显示了最终的菜单程序
/*menuette.c--菜单技术*/
#include <stdio.h>
char get_choice (void);
char get_first (void);
int get_int (void);
void count (void);
int main (void)
{
int choice;
void count (void);
while ((choice=get_choice())!='q')
{
switch (choice)
{
case 'a':printf("Buy low ,sell high.\n");
break;
case 'b':putchar('\a');
break;
case 'c':count();
break;
default:printf("Program error!\n");
break;
}
}
printf("Bye.\n");
return 0;
}
void count(void)
{
int n,i;
printf("Count how far?Enter an integer: \n");
n=get_int();
for (i=1;i<=n;i++)
printf("%d\n",i);
while(getchar()!='\n')
continue;
}
char get_choice(void)
{
int ch;
printf("Enter the letter of your choice:\n");
printf("a.advice b.bell\n");
printf("c.count q.quit\n");
ch = get_first ();
while((ch<'a' || ch>'c') && ch!='q')
{
printf("Please respond with a,b,c, or q.\n");
ch=get_first();
}
return ch;
}
char get_first(void)
{
int ch;
ch=getchar();
while(getchar()!='\n')
continue;
return ch;
}
int get_int(void)
{
int input;
char ch;
while(scanf("%d",&input)!=1)
{
while((ch=getchar())!='\n')
putchar(ch); //剔除错误的输入
printf(" is not an integer.\nPlease enter an ");
printf("integer value,such as 25,-178,or 3: ");
}
return input;
}
让菜单界面按照您所希望的那样顺利工作是很难的,但在你开发了一种可行的方法后,您就可以在多种情况下重用该界面。
另外要注意的一点是在面临较复杂的任务时,每个函数如何将任务指派给其他函数,这样就可以使程序更加模块化。
来源:oschina
链接:https://my.oschina.net/u/2754880/blog/704452