V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
BRS5672023
V2EX  ›  C++

C++ 新手想问下关于 Linux 下如何实现类似 Windows 的 Pause 功能的问题

  •  
  •   BRS5672023 · 18 天前 · 1257 次点击

    最近几天看了一点黑马程序员的视频(没什么基础),试着跟着写了一下实现通讯录管理的代码(暂时只完成了一部分功能),想要在 Linux 系统下实现类似 Windows 里 system("pause") 的功能,比如在通讯录中写完一个联系人的信息或者显示一个联系人的信息的时候能够 Pause 一下。。代码如下

    #include <iostream>
    using namespace std;
    
    #include <string>
    #include <limits> // 定义 pause 函数以及 getValidInput 函数中使用
    
    #define MAX 1000 // 通讯录最大人数
    
    void showMenu ();
    void pause ();
    
    int getValidInput ();
    
    // 联系人结构体
    struct Person {
        string m_Name;
        int m_Gender;
        int m_Age;
        long m_Phone;
        string m_Address;
    };
    
    // 通讯录结构体
    struct Addressbooks {
        struct Person personArray[MAX];
        int m_Size; // 通讯录中人员个数
    };
    
    void addPerson (Addressbooks *abs);
    void showPerson (Addressbooks *abs);
    
    int main () {
        struct Addressbooks abs;
        abs.m_Size = 0;
    
        while (true) {
            showMenu(); // 菜单调用
            // cin >> select;
    
            // 检查输入是否有效
            // if (cin.fail()) {
            //     cin.clear(); // 清除错误状态
            //     cin.ignore(numeric_limits<streamsize>::max(),'\n'); // 清空缓冲区
            //     cout << "请输入有效的数字!" << endl;
            //     continue; // 重新进入循环
            // }
            int select = getValidInput ();
    
            switch (select) {
                case 1: // 添加联系人
                    addPerson(&abs); // 利用地址传递,可以修饰实参
                    break;
                case 2:
                    showPerson(&abs);
                    break;
                case 3:
                    break;
                case 4:
                    break;
                case 5:
                    break;
                case 6:
                    break;
                case 0:
                    cout << "欢迎下次使用" << endl;
                    // pause();
                    return 0;
                    // break;
                default:
                    cout << "请输入数字 1-6 或 0" << endl;
                    break;
            }
        }
    
        return 0;
    }
    
    void showMenu () {
        cout << "1 、添加联系人" << endl;
        cout << "2 、显示联系人" << endl;
        cout << "3 、删除联系人" << endl;
        cout << "4 、查找联系人" << endl;
        cout << "5 、修改联系人" << endl;
        cout << "6 、清空联系人" << endl;
        cout << "0 、退出通讯录" << endl;
    }
    
    // 实现类似 Windows 系统上的 system("pause") 功能
    void pause () {
        cin.clear();
        // if (cin.eof()) {
            cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略输入流中的内容直到换行符,但因为输入内容已经被清空,这里程序会等待输入一个回车(换行符)
        // }
        cout << "请按回车键继续" << endl;
        cin.get();
    }
    
    // 检测输入数据是否为整数,是则返回该输入
    int getValidInput () {
        int input;
        while (true) {
            cin >> input;
            if (cin.fail()) {
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(), '\n');
                cout << "请输入有效的数字!" << endl;
            }
            else {
                if (cin.eof()) {
                    cin.ignore(numeric_limits<streamsize>::max(), '\n');
                }
                return input;
            }
        }
    }
    
    // 添加联系人
    void addPerson (Addressbooks *abs) {
        // 判断通讯录是否已满
        if (abs->m_Size == MAX) {
            cout << "通讯录已满,无法添加联系人!" << endl;
            return;
        }
        else {
            string name;
            cout << "请输入联系人姓名:" << endl;
            cin >> name;
            abs->personArray[abs->m_Size].m_Name = name;
    
            cout << "请输入联系人性别:" << endl;
            cout << "输入数字 1 为男性" << endl;
            cout << "输入数字 2 为女性" << endl;
    
            while (true) {
                // cin >> gender;
    
                // if (cin.fail()) {
                //     cin.clear();
                //     cin.ignore(numeric_limits<streamsize>::max(), '\n');
                //     cout << "请输入有效的数字!" << endl;
                //     continue;
                // }
    
                int gender = getValidInput ();
    
                if (gender == 1 || gender == 2) {
                    abs->personArray[abs->m_Size].m_Gender = gender;
                    break;
                }
    
                cout << "输入有误,请重新输入" << endl;
            }
    
            cout << "请输入联系人年龄:" << endl;
            int age = getValidInput ();
            abs->personArray[abs->m_Size].m_Age = age;
    
            cout << "请输入联系人电话号码:" << endl;
            long phone;
            cin >> phone;
            abs->personArray[abs->m_Size].m_Phone = phone;
    
            cout << "请输入联系人家庭住址:" << endl;
            string address;
            cin >> address;
            abs->personArray[abs->m_Size].m_Address = address;
    
            cout << "已成功添加联系人 " << abs->personArray[abs->m_Size].m_Name << endl;
            abs->m_Size++;
    
            pause();
            system("clear"); // 对于 Windows 系统,应该使用 system("cls")
        }
    }
    
    void showPerson (Addressbooks *abs) {
        if (abs->m_Size == 0) {
            cout << "当前记录为空" << endl;
        }
        else {
            for (int i=0;i<abs->m_Size;i++) {
                cout << "姓名:" << abs->personArray[i].m_Name << endl;
                cout << "性别:" << abs->personArray[i].m_Gender << endl;
                cout << "年龄:" << abs->personArray[i].m_Age << endl;
                cout << "电话:" << abs->personArray[i].m_Phone << endl;
                cout << "地址:" << abs->personArray[i].m_Address << endl;
            }
        }
        pause();
        system("clear");
    }
    

    然后我发现在我定义的函数 getValidInput 里面加入一个 cin.eof() 的判断就基本实现了我想要的效果,但其实我不太理解原因是什么,特别是如果我把 getValidInput 函数的这个判断给注释掉的话,那么在我输入 "2" 使用 "显示联系人" 这个功能时,需要再输入一个回车才会显示 "请按回车键继续" 的字符;而如果我仍然注释 getValidInput 中的这个判断,但取消 pause 函数定义中关于 cin.eof() 的注释(见上面的代码),那么在我输入 "1" 使用 "添加联系人" 的功能的时候,在我输入完联系人信息之后,pause() 似乎不会起作用,system("clear") 会直接清屏。。

    10 条回复    2024-10-19 18:48:55 +08:00
    cnbatch
        1
    cnbatch  
       18 天前
    system()本质是运行命令行的命令,所以 system("pause")意思就是在 Windows 调用 pause 命令

    如果是想通用一点,那就把 system("pause")替换成
    printf("Press enter to continue");
    int c = getchar();
    ysc3839
        2
    ysc3839  
       18 天前 via Android
    好像要关掉行缓冲,然后就能接收到单个字符输入了,收到之后继续运行即可。
    yanqiyu
        3
    yanqiyu  
       18 天前
    general: cin.eof 除非我按 Ctrl+D ,或者 pipe 进来个文件,否则始终是 false ,所以有那个判断大概就是等价于删掉它包起来的代码。(并且一般交互式程序遇到 end of cin 大概就该结束了)

    > 特别是如果我把 getValidInput 函数的这个判断给注释掉的话,那么在我输入 "2" 使用 "显示联系人" 这个功能时,需要再输入一个回车才会显示 "请按回车键继续" 的字符

    cin.ignore(numeric_limits<streamsize>::max(), '\n'); 会一直等输入(堵塞)直到遇到回车,要是缓冲区里面没有回车的话。

    > 而如果我仍然注释 getValidInput 中的这个判断,但取消 pause 函数定义中关于 cin.eof() 的注释(见上面的代码),那么在我输入 "1" 使用 "添加联系人" 的功能的时候,在我输入完联系人信息之后,pause() 似乎不会起作用,system("clear") 会直接清屏。。

    那大概是缓冲区里面 somehow 留了一个\n...要是没有前面的 ignore 把它耗掉它就把那个 std::cin.get() pass 掉了
    wshcdr
        4
    wshcdr  
       18 天前
    #include <iostream>

    int main() {
    std::cout << "Press any key to continue..." << std::endl;
    std::cin.get(); // 等待用户按下一个键
    std::cout << "Continuing..." << std::endl;
    return 0;
    }
    sagaxu
        5
    sagaxu  
       18 天前   ❤️ 1
    system("read -n 1")
    BRS5672023
        6
    BRS5672023  
    OP
       18 天前
    @cnbatch 直接按照你给出的说法替换后在我进行添加联系人的操作后的输出结果是

    Press enter to continue1 、添加联系人
    2 、显示联系人
    3 、删除联系人
    4 、查找联系人
    5 、修改联系人
    6 、清空联系人
    0 、退出通讯录

    不太清楚怎么回事。。
    BRS5672023
        7
    BRS5672023  
    OP
       18 天前
    @sagaxu 这个方法不错,感谢
    BRS5672023
        8
    BRS5672023  
    OP
       18 天前
    @yanqiyu

    >> 特别是如果我把 getValidInput 函数的这个判断给注释掉的话,那么在我输入 "2" 使用 "显示联系人" 这个功能时,需要再输入一个回车才会显示 "请按回车键继续" 的字符

    > cin.ignore(numeric_limits<streamsize>::max(), '\n'); 会一直等输入(堵塞)直到遇到回车,要是缓冲区里面没有回车的话。

    这个地方为什么它在 case 1 和 case 2 的时候行为不一致呢?是因为我在使用 case 2 的 showPerson 函数里面没有任何输入所以需要我先输入一个回车吗?然后我加入的 cin.eof() 的判断就相当于把 cin.ignore 这一行给注释掉了所以在我一开始选择 case 的 getValidInput 里就会把我最初输入的 1 或者 2 给保留下来,然后 case 2 里的 pause 就不再需要我再输入一个换行符了,大概是这个原因?
    zwzwzwzwzxt
        9
    zwzwzwzwzxt  
       17 天前
    @sagaxu #5 read 是 shell 内建的命令,最好 "bash -c 'read -n 1'" 指定一下不然可能会有问题。
    yanqiyu
        10
    yanqiyu  
       17 天前
    @BRS5672023 #8 一般我也不会太细究为什么,毕竟研究 iobuffer 行为大概率会变成无用功(这方面折腾不如用现成的 TUI 库),但是原因大概是 cin 的流操作会留一个\n 在 buffer 里面(对应的 getline 不会)

    可能有些路径没有最后一个(输入的)换行之前用的 cin >>, 然后\n 就留给了最后的 cin.get();然后退出了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5822 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 02:50 · PVG 10:50 · LAX 18:50 · JFK 21:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.