如何在ANSI/ISO C中编写控制台菜单?

5

我正在尝试使用C语言创建一个简单的程序,用户需要在几个选项中进行选择:

char command = '1';
while(command!='0') {
    printf("Menu:\n");
    printf("1. First option\n");
    printf("2. Second option\n");
    printf("0. Exit\n");
    printf("Choose: 0,1,2?: ");
    command = getchar();
    while(getchar()!='\n');     
    switch(command) {
        case '0': break;
        case '1': functionCall1(); break; 
        case '2': functionCall2(); break;
    }
}

我的代码问题在于,每隔一次输入1、2或0时,什么都不会发生,只有菜单再次打印出来。使用调试器可以看到,command变量的值,在执行command = getchar()语句后,每隔一次会变成空值。我想应该吃掉换行符就足够了吧?


2
你的示例在我的电脑上运行良好,如果我让functionCall1和functionCall2打印一些东西,我可以看到它按照广告所说的那样工作。但是也许你的编译器不同? - Mr Lister
3
command 的类型必须是 int 而不是 char,以便能够保存 EOF。请注意,当有人输入 EOF(例如在 Unix 上按 Ctrl-D),您的程序会在 while (getchar() != '\n') ; 循环中快速执行。 - Jens
2
一个 do { ... } while('0' != command); 会是一种更优雅的结构。 - guga
1
@guga 我并不认为 Yoda 条件非常优雅。 - Mr Lister
是的,我使用简化的functionCall1和functionCall2进行了测试,它正常工作,显然问题出在我的函数上。谢谢大家。 - mjekov
@MrLister,你的评论得-1分!:p 没有什么比正确的结构更能提高程序可读性了!当然,需要理解何时使用适当的结构。 - guga
3个回答

5

尝试我的自定义菜单,我实现它是为了在处理包含多个选择操作的程序时更加方便。 菜单是可导航的(箭头:向上,向下,向左,向右),并且只需按Enter键即可进行选择,菜单方向可以垂直或水平设置,填充可以设置为一组项目(子项),子项起始位置和延迟更新。

菜单调用示例(垂直):

int response = menu("OPTIONS","[*]","->",
                    1,3,3,0,5,
                    "PROFILES","ACTIVITY","VIDEO","SOUND","GAMEPLAY");

最重要的是函数实现只需要60行代码。

菜单实现:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>

// LXSoft
// mod: cui/menu_021
// stdarg.h  -> used for variable list of arguments (va_list, va_start ...)
// windows.h -> used for Sleep function, for *nix use unistd.h

typedef unsigned short int usint_t;
// Menu function prototype
int menu(char* name, char* prefix, char* cursor, usint_t orientation,
         usint_t padding, usint_t start_pos, usint_t delay,
         usint_t num_childs, ...);

int main()
{
    int response = menu("OPTIONS","[*]","->",1,3,3,0,5,
                        "PROFILES","ACTIVITY","VIDEO","SOUND","GAMEPLAY");
    switch(response)
    {
        case 1:
            // doSomethingFoo1();
        break;
        case 2:
            //doSomethingFoo2();
        break;
        /*
         * .
         * .
         * .
         * case n:
         * break;
         */
    }
    printf("\nYour choice is: %d", response);
    return 0;
}

// Menu implementation
int menu
(
    char *name,        // Menu name (eg.: OPTIONS)
    char *prefix,      // Menu prefix (eg.: [*])
    char *cursor,      // Menu cursor (eg.: ->)
    usint_t orient,    /*
                        * Menu orientation vertical or horzontal.
                        * 0 or false for horizontal
                        * 1 or true for vertical
                        */
    usint_t padding,   // Menu childrens padding (eg.: 3)
    usint_t start_pos, // Menu set active child (eg.: 1)
    usint_t delay,     // Menu children switch delay
    usint_t childs,    // Number of childrens
    ...                /*
                        * Variable list of arguments char* type.
                        * Name of the childrens.
                        */
)
{
    va_list args;
    int tmp=0,pos;
    char chr;
    usint_t opt=start_pos;
    char* format=malloc
    (
        (
            strlen(name)+strlen(prefix)+strlen(cursor)+
            3+ /* menu suffix (1 byte) and backspace (2 bytes) */
            (2*childs)+ /* newline (2 bytes) times childs */
            (padding*childs)+ /* number of spaces times childs */
            childs*15 /* children name maxlen (15 bytes) times childs*/
        )*sizeof(char)
    );
    do
    {
        if(tmp!=0)chr=getch();
        if(chr==0x48||chr==0x4B)
            (opt>1&&opt!=1)?opt--:(opt=childs);
        else if(chr==0x50||chr==0x4D)
            (opt>=1&&opt!=childs)?opt++:(opt=1);
        else {/* do nothing at this time*/}
        strcpy(format,"");
        strcat(format,prefix);
        strcat(format,name);
        strcat(format,":");
        va_start(args,childs);
        for (tmp=1;tmp<=childs;tmp++)
        {
            (orient)?strcat(format,"\n"):0;
            pos=padding;
            while((pos--)>0) strcat(format," ");
            if(tmp==opt)
            {
                strcat(format,"\b");
                strcat(format,cursor);
            }
            strcat(format,va_arg(args,char*));
        }
        /*if(tmp!=childs)
        {
            fprintf(stderr,"%s: recieved NULL pointer argument,"
                           " child not named", __func__);
            return -1;
        }*/
        Sleep(delay);
        system("cls");
        printf(format);
        va_end(args);
    }while((chr=getch())!=0x0D);
    return opt;
}

1

也许你应该尝试使用int x作为关键字来使用所需的命令,就像这样:

while(x != 0)
{
    scanf("%d", &x);
    switch (x)
    {
        printf("input '2' to...\n");
        printf("input '3' to...\n");
        printf("input '4' to...\n");
        printf("input '5' to...\n");
        printf("input '6' to...\n");
        case 1:
            head = Enqueue(head);
            break;
        case 2:
            head1 = spisokMagazinovScenoiMensheiZadannoi(head, head1);
            break;
        case 3:
            head1 = udalenieElementa(head1);
            break;
        case 4:
            head1 = addNewMagazin(head1);
            break;
        case 5:
            head1 = addNewMagazin(head1);
            break;
        case 6:
            printToTheFile(head);
            break;

    }
}

我在之前的作业中使用过它。希望它对你有用。


0

这是我的函数菜单示例。

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>

int menu(char s[][80],int kolop){
    int pos = 0,push = 0,i,flag = 0;
    printf("MENU:\n>");
    puts(s[0]);
    for(i = 1;i < kolop;i++){
        printf(" ");
        puts(s[i]);
    }
    printf("Enter 5 to move the cursor down 8 to the top(Press ENTER to end)\n");
    do{
            if(flag == 1)
              printf("Error try to press 5,8 or ENTER\n");
            push = getch();
            flag = 1;
    }
    while(push != 56 && push != 53 && push != 13);
    flag = 0;
    system("cls");
    while(push != 13){
        if(push == 53){
            puts("MENU:");
            for(i = 0;i < kolop;i++){
                if(i == pos + 1 && flag == 0){
                    printf(">");
                    puts(s[i]);
                    flag = 1;
                    pos++;
                }
                else{
                    printf(" ");
                    puts(s[i]);
                }
            }
        }
        if(push == 56){
            puts("MENU:");
            for(i = 0;i < kolop;i++){
                if(i == pos - 1 && flag == 0){
                    printf(">");
                    puts(s[i]);
                    flag = 1;
                    pos--;
                }
                else{
                    printf(" ");
                    puts(s[i]);
                }
            }
        }
        _flushall();
        printf("Enter 5 to move the cursor down 8 to the top(Press ENTER to end)\n");
        flag = 0;
        do{
            if(flag == 1)
              printf("Error try to press 5,8 or ENTER\n");
            push = getch();
            flag = 1;
        }
        while(push != 56 && push != 53 && push != 13);
        flag = 0;
        system("cls");
    }
    pos++;
    return pos;
}

int main()
{
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    char s[][80] = {"1","2","3","4","5","6","7"};
    int i,pos = 0,push,flag = 0;
    pos = menu(s,7);
    printf("%d",pos);
    //system("cls");
  return 0;
}

1
Nit,printf(“MENU:\n>”)中需要转换吗?用非变参的方式,fputs("MENU\n>",stdout); 不是同样可以实现吗?(是的,优秀的编译器能为你进行优化)。system(“cls”); 是100%不可移植且不必要的(它只输出一个屏幕大小的'\n'字符)。char s [] [80]对于包括'\0'字符在内的14个字节的数据似乎有点过多了(不清楚为什么使用单个字符的字符串字面量)。585313是什么?你是说'5'':''\r'吗?如果是,请使用字符字面量(更易读)。 - David C. Rankin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接