代码之家  ›  专栏  ›  技术社区  ›  Adam

从标准输入中捕获字符,而不等待按下Enter键

  •  145
  • Adam  · 技术社区  · 16 年前

    我永远都记不起我是怎么做到的,因为这对我来说太少了。但是在C或C++中,从标准输入中读取字符的最好方法是不等待换行符(按Enter)。

    理想情况下,它不会将输入字符反射到屏幕上。我只想在不影响控制台屏幕的情况下捕获击键。

    15 回复  |  直到 6 年前
        1
  •  88
  •   Johannes Schaub - litb    14 年前

    这在纯C++中是不可能的,因为它太依赖于可能与STDIN连接的终端(它们通常是行缓冲的)。但是,您可以使用一个库:

    1. Conio可用于Windows编译器。使用函数 _getch() 在不等待Enter键的情况下给您一个字符。我不是一个经常使用Windows的开发人员,但我见过我的同学只包括 conio.h 并使用它。见 conio.h 在维基百科。它列出 getch 这是在Visual C++中声明的。
    2. Curses可用于Linux,兼容的Curses实现也可用于Windows。它还有一个 格奇 功能。(试一试 man getch 查看其手册页)。见 Curses 在维基百科。

    如果您的目标是跨平台兼容性,我建议您使用curses。也就是说,我相信有一些功能可以用来关闭线路缓冲(我相信这被称为“原始模式”,而不是“烹饪模式”(查看 man stty )如果我没弄错的话,诅咒者会用一种便携的方式为你处理。

        2
  •  74
  •   Falcon Momot    12 年前

    在Linux(和其他类似于Unix的系统)上,可以通过以下方式完成:

    #include <unistd.h>
    #include <termios.h>
    
    char getch() {
            char buf = 0;
            struct termios old = {0};
            if (tcgetattr(0, &old) < 0)
                    perror("tcsetattr()");
            old.c_lflag &= ~ICANON;
            old.c_lflag &= ~ECHO;
            old.c_cc[VMIN] = 1;
            old.c_cc[VTIME] = 0;
            if (tcsetattr(0, TCSANOW, &old) < 0)
                    perror("tcsetattr ICANON");
            if (read(0, &buf, 1) < 0)
                    perror ("read()");
            old.c_lflag |= ICANON;
            old.c_lflag |= ECHO;
            if (tcsetattr(0, TCSADRAIN, &old) < 0)
                    perror ("tcsetattr ~ICANON");
            return (buf);
    }
    

    基本上,您必须关闭规范模式(和回声模式以抑制回声)。

        3
  •  15
  •   chrissidtmeier    7 年前

    科尼奥

    您需要的功能是:

    int getch();
    Prototype
        int _getch(void); 
    Description
        _getch obtains a character  from stdin. Input is unbuffered, and this
        routine  will  return as  soon as  a character is  available  without 
        waiting for a carriage return. The character is not echoed to stdout.
        _getch bypasses the normal buffering done by getchar and getc. ungetc 
        cannot be used with _getch. 
    Synonym
        Function: getch 
    
    
    int kbhit();
    Description
        Checks if a keyboard key has been pressed but not yet read. 
    Return Value
        Returns a non-zero value if a key was pressed. Otherwise, returns 0.
    

    利比科尼奥 http://sourceforge.net/projects/libconio

    Linux C++的CONIO.H实现 http://sourceforge.net/projects/linux-conioh

        4
  •  14
  •   cwhiii    15 年前

    我在另一个论坛上发现这个问题,同时也在寻找解决相同问题的方法。我对它做了一些修改。效果很好。我运行的是OSX,所以如果你运行的是微软,你需要找到正确的system()命令来切换到生的和熟的模式。

    #include <iostream> 
    #include <stdio.h>  
    using namespace std;  
    
    int main() { 
      // Output prompt 
      cout << "Press any key to continue..." << endl; 
    
      // Set terminal to raw mode 
      system("stty raw"); 
    
      // Wait for single character 
      char input = getchar(); 
    
      // Echo input:
      cout << "--" << input << "--";
    
      // Reset terminal to normal "cooked" mode 
      system("stty cooked"); 
    
      // And we're out of here 
      return 0; 
    }
    
        5
  •  8
  •   Joseph Dykstra J. K.    8 年前
    #include <conio.h>
    
    if (kbhit() != 0) {
        cout << getch() << endl;
    }
    

    这种用途 kbhit() 检查键盘是否被按下并使用 getch() 获取正在被按下的角色。

        6
  •  7
  •   hasen    16 年前

    如果您在Windows上,可以使用 PeekConsoleInput 检测是否有输入,

    HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
    DWORD events;
    INPUT_RECORD buffer;
    PeekConsoleInput( handle, &buffer, 1, &events );
    

    然后使用readconsoleinput“使用”输入字符。

    PeekConsoleInput(handle, &buffer, 1, &events);
    if(events > 0)
    {
        ReadConsoleInput(handle, &buffer, 1, &events);  
        return buffer.Event.KeyEvent.wVirtualKeyCode;
    }
    else return 0
    

    老实说,这是我的一些旧代码,所以你必须稍微修改一下。

    不过,很酷的是,它在不提示任何内容的情况下读取输入,因此根本不显示字符。

        7
  •  5
  •   Tritium    16 年前

    假设是窗户,看看 读控制台输入 功能。

        8
  •  5
  •   David Thornley    16 年前

    C和C++对I/O有一个非常抽象的视图,没有标准的方法来做你想做的事情。有标准的方法从标准输入流中获取字符(如果有需要的话),并且这两种语言都没有定义其他任何字符。因此,任何答案都必须是平台特定的,可能不仅取决于操作系统,还取决于软件框架。

    这里有一些合理的猜测,但如果不知道目标环境是什么,就无法回答您的问题。

        9
  •  5
  •   Norman Ramsey    16 年前

    最接近便携式的是使用 ncurses 库将终端置于“cbreak模式”。API是巨大的;您最需要的例程是

    • initscr endwin
    • cbreak nocbreak
    • getch

    祝你好运!

        10
  •  4
  •   PolyThinker    16 年前

    以下是从中提取的溶液 专家C编程:深层次的秘密 ,应该在SVR4上工作。它使用 斯蒂 IOCTL .

    #include <sys/filio.h>
    int kbhit()
    {
     int i;
     ioctl(0, FIONREAD, &i);
     return i; /* return a count of chars available to read */
    }
    main()
    {
     int i = 0;
     intc='';
     system("stty raw -echo");
     printf("enter 'q' to quit \n");
     for (;c!='q';i++) {
        if (kbhit()) {
            c=getchar();
           printf("\n got %c, on iteration %d",c, i);
        }
    }
     system("stty cooked echo");
    }
    
        11
  •  2
  •   Setu Gupta    10 年前

    我一直想要一个循环来读取我的输入,而不需要按回车键。 这对我有用。

    #include<stdio.h>
     main()
     {
       char ch;
        system("stty raw");//seting the terminal in raw mode
        while(1)
         {
         ch=getchar();
          if(ch=='~'){          //terminate or come out of raw mode on "~" pressed
          system("stty cooked");
         //while(1);//you may still run the code 
         exit(0); //or terminate
         }
           printf("you pressed %c\n ",ch);  //write rest code here
          }
    
        }
    
        12
  •  2
  •   user1438233    9 年前

    在Windows上为我工作:

    #include <conio.h>
    char c = _getch();
    
        13
  •  2
  •   ssinfod    9 年前

    我使用kbhit()查看是否存在字符,然后使用getchar()读取数据。 在窗户上,你可以用“conio.h”。在Linux上,您必须实现自己的kbhit()。

    见下面的代码:

    // kbhit
    #include <stdio.h>
    #include <sys/ioctl.h> // For FIONREAD
    #include <termios.h>
    #include <stdbool.h>
    
    int kbhit(void) {
        static bool initflag = false;
        static const int STDIN = 0;
    
        if (!initflag) {
            // Use termios to turn off line buffering
            struct termios term;
            tcgetattr(STDIN, &term);
            term.c_lflag &= ~ICANON;
            tcsetattr(STDIN, TCSANOW, &term);
            setbuf(stdin, NULL);
            initflag = true;
        }
    
        int nbbytes;
        ioctl(STDIN, FIONREAD, &nbbytes);  // 0 is STDIN
        return nbbytes;
    }
    
    // main
    #include <unistd.h>
    
    int main(int argc, char** argv) {
        char c;
        //setbuf(stdout, NULL); // Optional: No buffering.
        //setbuf(stdin, NULL);  // Optional: No buffering.
        printf("Press key");
        while (!kbhit()) {
            printf(".");
            fflush(stdout);
            sleep(1);
        }
        c = getchar();
        printf("\nChar received:%c\n", c);
        printf("Done.\n");
    
        return 0;
    }
    
        14
  •  1
  •   Ruchira    12 年前

    您可以使用SDL(简单的DirectMedia库)进行移植,尽管我怀疑您可能不喜欢它的行为。当我尝试它时,我必须让SDL创建一个新的视频窗口(即使我的程序不需要它),并让这个窗口“抓取”几乎所有的键盘和鼠标输入(这对我的使用是可以的,但在其他情况下可能是烦人或不可行的)。我怀疑这太过分了,不值得这样做,除非必须具备完全的可移植性——否则试试其他建议的解决方案。

    顺便说一下,如果你喜欢的话,这会给你单独的按键和发布事件。

        15
  •  1
  •   AngryDane    6 年前

    Ncurses提供了一个很好的方法来实现这一点! 这也是我的第一篇文章(我记得),所以欢迎任何评论。我会感谢有用的,但欢迎大家!

    编译:G++-STD= C++ 11—pTrim-LncEclis.CPP-O

    #include <iostream>
    #include <ncurses.h>
    #include <future>
    
    char get_keyboard_input();
    
    int main(int argc, char *argv[])
    {
        initscr();
        raw();
        noecho();
        keypad(stdscr,true);
    
        auto f = std::async(std::launch::async, get_keyboard_input);
        while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
        {
            // do some work
        }
    
        endwin();
        std::cout << "returned: " << f.get() << std::endl;
        return 0;
    }
    
    char get_keyboard_input()
    {
        char input = '0';
        while(input != 'q')
        {
            input = getch();
        }
        return input;
    }