【问题标题】:C++ Detect when user presses arrow keyC ++检测用户何时按下箭头键
【发布时间】:2022-01-12 00:15:33
【问题描述】:

我在检测 C++ 控制台应用程序中的箭头键按下时遇到了问题。我已经尝试了我在这里和其他教程网站上找到的所有内容,但是每当我按下箭头时,它们都会给我同样的东西:

Process returned 0 <0x0> execution time : 2.249 s
Press any key to continue.

这是我尝试过的所有检测按键的方法,结果都是一样的。这些是我的代码中仅剩的两个,其他我尝试删除而不是注释掉。

方法一:

c1 = getch();
if(c1 == 0)
{

    c2 = getch();

    if(c2 == 72) {cout << endl << "Up Arrow" << endl;}
    else if(c2 == 80) {cout << endl << "Down Arrow" << endl;}
    else{cout << endl << "Incorrect Input" << endl;}

}

方法二:

switch(getch()) {
case 65:
       cout << endl << "Up" << endl;//key up
    break;
case 66:
    cout << endl << "Down" << endl;   // key down
    break;
case 67:
    cout << endl << "Right" << endl;  // key right
    break;
case 68:
    cout << endl << "Left" << endl;  // key left
    break;
}

我的代码中是否有一些错误让我回到我的主要方法,或者它是否跳过了一些代码?有没有更快的方法来做到这一点?我几乎 100% 确定我的其他代码与此问题没有任何关系,因为我将代码与程序的任何其他方面隔离开来,并且我一直遇到同样的问题。

再次,我尝试了所有我能找到的获取箭头键的方法,但我一直遇到同样的问题。如果重要的话,我使用的是 Windows 8 Samsung ATIV Smart PC 并使用键盘底座。

提前感谢您的帮助。

【问题讨论】:

  • 你从哪里得到这些字符代码的?它们只是大写字母,例如,尝试在第一个示例中按 H,在第二个示例中按 A,看看会发生什么。
  • @PeterJ,这是getch 的福利。不过,ReadConsoleInput 会是更合适的选择。
  • @chris,我在哪里可以找到一个很好的例子?
  • this one 怎么样?
  • 答案可能在这个questions top answers

标签: c++ windows-8 console-application arrow-keys


【解决方案1】:
#include <conio.h>
#include <iostream>
using namespace std;

#define KEY_UP 72
#define KEY_DOWN 80
#define KEY_LEFT 75
#define KEY_RIGHT 77

int main()
{
    int c = 0;
    while(1)
    {
        c = 0;

        switch((c=getch())) {
        case KEY_UP:
            cout << endl << "Up" << endl;//key up
            break;
        case KEY_DOWN:
            cout << endl << "Down" << endl;   // key down
            break;
        case KEY_LEFT:
            cout << endl << "Left" << endl;  // key left
            break;
        case KEY_RIGHT:
            cout << endl << "Right" << endl;  // key right
            break;
        default:
            cout << endl << "null" << endl;  // not arrow
            break;
        }

    }

    return 0;
}

输出如下:

Up

Down

Right

Left

Up

Left

Right

Right

Up

检测到方向键按下!

【讨论】:

  • 非常感谢!我测试了您的确切代码,它运行良好(减去一个缺少的花括号),它给了我一个工作结果!现在我将它放入我的代码中,看看它是否会继续工作
  • Visual Studio 2013 告诉我使用 _getch() 因为 getch() 已被弃用。 “警告 C4996:'getch':不推荐使用此项目的 POSIX 名称。改为使用符合 ISO C++ 的名称:_getch。”
  • 不同的语言环境怎么样?这也适用于中文布局的键盘吗?
  • arbboter 的回答让您走上了正确的道路,但没有考虑到箭头键的扫描码实际上返回了两个值。您可以查看我的答案或以下三个答案中的任何一个以获取更多信息:stackoverflow.com/a/10473315/130614stackoverflow.com/a/2877857/130614stackoverflow.com/a/16510089/130614
  • 不要使用 conio.h 它的非标准和非常旧的头文件,现代编译器/IDE 不支持。
【解决方案2】:

arbboter 的上一个答案很接近,但忽略了箭头键(和其他特殊键)返回两个字符的扫描码的事实。第一个是 (0) 或 (224),表示密钥是扩展密钥;第二个包含扫描码值。

如果不考虑这一点,“H”、“K”、“M”和“P”的 ASCII 值会被误解为“上”、“下”、“左”和“右”。

这是 arbboter 代码的修改版本,用于演示按下箭头键之一时读取扩展值:

#include <conio.h>
#include <iostream>
using namespace std;

#define KEY_UP    72
#define KEY_LEFT  75
#define KEY_RIGHT 77
#define KEY_DOWN  80

int main()
{
    int c, ex;

    while(1)
    {
        c = getch();

        if (c && c != 224)
        {
            cout << endl << "Not arrow: " << (char) c << endl;
        }
        else
        {
            switch(ex = getch())
            {
                case KEY_UP     /* H */:
                    cout << endl << "Up" << endl;//key up
                    break;
                case KEY_DOWN   /* K */:
                    cout << endl << "Down" << endl;   // key down
                    break;
                case KEY_LEFT   /* M */:
                    cout << endl << "Left" << endl;  // key left
                    break;
                case KEY_RIGHT: /* P */
                    cout << endl << "Right" << endl;  // key right
                    break;
                default:
                    cout << endl << (char) ex << endl;  // not arrow
                    break;
            }
        }
    }

    return 0;
}

【讨论】:

    【解决方案3】:
    // Example for inputting a single keystroke in C++ on Linux
    // by Adam Pierce <adam@doctort.org> on http://www.doctort.org/adam/nerd-notes/reading-single-keystroke-on-linux.html
    // This code is freeware. You are free to copy and modify it any way you like.
    // Modify by me Putra Kusaeri
    
    
    #include <iostream>
    #include <termios.h>
    #define STDIN_FILENO 0
    using namespace std;
    int main()
    {
    // Black magic to prevent Linux from buffering keystrokes.
        struct termios t;
        tcgetattr(STDIN_FILENO, &t);
        t.c_lflag &= ~ICANON;
        tcsetattr(STDIN_FILENO, TCSANOW, &t);
    
    // Once the buffering is turned off, the rest is simple.
        cout << "Enter a character: ";
        char c,d,e;
        cin >> c;
        cin >> d;
        cin >> e;
        cout << "\nYour character was ";
    // Using 3 char type, Cause up down right left consist with 3 character
        if ((c==27)&&(d=91)) {
            if (e==65) { cout << "UP";}
            if (e==66) { cout << "DOWN";}
            if (e==67) { cout << "RIGHT";}
            if (e==68) { cout << "LEFT";}
        }
        return 0;
    }
    

    reference

    【讨论】:

    【解决方案4】:

    这里是一种不使用 getch() 的替代方法,使用 events (评论很好,我试图让它尽可能简单)

    #include <iostream>
    #include <Windows.h>
    
    int main(int argc, char *argv[]){
    
        HANDLE rhnd = GetStdHandle(STD_INPUT_HANDLE);  // handle to read console
    
        DWORD Events = 0;     // Event count
        DWORD EventsRead = 0; // Events read from console
    
        bool Running = true;
    
        //programs main loop
        while(Running) {
    
            // gets the systems current "event" count
            GetNumberOfConsoleInputEvents(rhnd, &Events);
    
            if(Events != 0){ // if something happened we will handle the events we want
    
                // create event buffer the size of how many Events
                INPUT_RECORD eventBuffer[Events];
    
                // fills the event buffer with the events and saves count in EventsRead
                ReadConsoleInput(rhnd, eventBuffer, Events, &EventsRead);
    
                // loop through the event buffer using the saved count
                for(DWORD i = 0; i < EventsRead; ++i){
    
                    // check if event[i] is a key event && if so is a press not a release
                    if(eventBuffer[i].EventType == KEY_EVENT && eventBuffer[i].Event.KeyEvent.bKeyDown){
    
                        // check if the key press was an arrow key
                        switch(eventBuffer[i].Event.KeyEvent.wVirtualKeyCode){
                            case VK_LEFT:
                            case VK_RIGHT:
                            case VK_UP:
                            case VK_DOWN:   // if any arrow key was pressed break here
                                std::cout<< "arrow key pressed.\n";
                                break;
    
                            case VK_ESCAPE: // if escape key was pressed end program loop
                                std::cout<< "escape key pressed.\n";
                                Running = false;
                                break;
    
                            default:        // no handled cases where pressed 
                                std::cout<< "key not handled pressed.\n";
                                break;
                        }
                    }
    
                } // end EventsRead loop
    
            }
    
        } // end program loop
    
        return 0;
    }
    

    (感谢评论者,我现在知道此代码不是标准代码,但如果您使用 g++ 编译它会起作用,更多信息在 cmets 中)

    【讨论】:

    • 此代码将在 INPUT_RECORD eventBuffer[Events]; 上失败,因为 Events 是非常量。但你可以一一阅读事件。
    • 我不知道Events 必须是const。我刚刚编译了它(使用g++),它似乎按照我的预期运行......无论如何,如果这是损坏的代码,我会尝试修复它或(很可能)只是删除答案,但在我这样做之前,我想知道这段代码到底应该如何失败,所以我可以尝试自己看看。
    • 动态数组是g++的扩展,不是标准的c++
    • 啊,我明白了,谢谢你的信息,我想我会做一个编辑。
    【解决方案5】:

    这里给出的一些答案没有考虑按下箭头键时会收到 2 个字符的事实。另外,需要注意的是输入字符应该是unsigned char。这是因为要确定是否按下了箭头键,我们使用 ASCII 值 224,它只能存储在 8 位字符(无符号字符)中,而不是 7 位有符号字符。

    您可以使用以下代码 sn-p。这里处理 2 种类型的输入。 ch1 是用户输入的第一个字符。这是用户正在输入的输入。但在箭头键的情况下,接收到 2 个字符的序列 ch1 和 ch2。 ch1 确定按下了某个箭头键,ch2 确定按下了特定的箭头键。

    const int KEY_ARROW_CHAR1 = 224;
    const int KEY_ARROW_UP = 72;
    const int KEY_ARROW_DOWN = 80;
    const int KEY_ARROW_LEFT = 75;
    const int KEY_ARROW_RIGHT = 77;
    
    unsigned char ch1 = _getch();
    if (ch1 == KEY_ARROW_CHAR1)
    {
        // Some Arrow key was pressed, determine which?
        unsigned char ch2 = _getch();
        switch (ch2) 
        {
        case KEY_ARROW_UP:
            // code for arrow up
            cout << "KEY_ARROW_UP" << endl;
            break;
        case KEY_ARROW_DOWN:
            // code for arrow down
            cout << "KEY_ARROW_DOWN" << endl;
            break;
        case KEY_ARROW_LEFT:
            // code for arrow right
            cout << "KEY_ARROW_LEFT" << endl;
            break;
        case KEY_ARROW_RIGHT:
            // code for arrow left
            cout << "KEY_ARROW_RIGHT" << endl;
            break;
        }
    }
    else
    {
        switch (ch1)
        {
            // Process other key presses if required.
        }
    }
    

    【讨论】:

      【解决方案6】:

      检查http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspxhttp://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx

      #include<windows.h>
      #include <stdio.h>
      
      int main()
      {
          HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
          DWORD NumInputs = 0;
          DWORD InputsRead = 0;
          bool running = true;
      
          INPUT_RECORD irInput;
      
          GetNumberOfConsoleInputEvents(hInput, &NumInputs);
      
          ReadConsoleInput(hInput, &irInput, 1, &InputsRead);
      
          switch(irInput.Event.KeyEvent.wVirtualKeyCode)
          {
              case VK_ESCAPE:
              puts("Escape");
              break;
      
              case VK_LEFT:
              puts("Left");
              break;
      
              case VK_UP:
              puts("Up");
              break;
      
              case VK_RIGHT:
              puts("Right");
              break;
      
              case VK_DOWN:
              puts("Down");
              break;
          } 
      
      }
      

      【讨论】:

      • 这个解决方案给了我同样的问题,我把代码放到一个新的项目文件中,它直接转到Process returned to 0。在这个例子中是什么阻止它直接到达Return 0;
      【解决方案7】:
      #include <iostream>
      #include <conio.h>
      
      const int KB_UP = 72;
      const int KB_DOWN = 80;
      const int KB_RIGHT = 77;
      const int KB_LEFT = 75;
      const int ESC = 27;
      
      int main() {
          
          while (true) {
              int ch = _getch();
              if (ch == 224) {
                  ch = _getch();
                  switch (ch) {
                  case KB_UP: 
                      std::cout << "up\n";   
                      break;
                  case KB_DOWN:
                      std::cout << "down\n";
                      break;
                  case KB_RIGHT:
                      std::cout << "right\n";
                      break;
                  case KB_LEFT:
                      std::cout << "left\n";
                      break;
                  default: std::cout << "unknown\n";
                  }
              }
              else if (ch == ESC)
              {
                  std::cout << "Escape pressed, going out!\n";
                  break;
              }
          }
      }
      

      这与上面的一些示例非常相似,只是我使用了

      _getchar()
      

      而不是

      getchar()
      

      那个视觉工作室(我用来编写和运行我的鳕鱼) 给出错误, 我也把它放在一个循环中,直到你按下 Escape 底部。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-11
        • 1970-01-01
        • 2011-08-01
        相关资源
        最近更新 更多