如何模拟“按任意键继续?”


87

我正在尝试编写一个C ++程序,其中当用户从键盘输入任何字符时,它应该移至下一行代码。

这是我的代码:

char c;

cin>>c;

cout<<"Something"<<endl;

但这是行不通的,因为只有当我输入一些字符然后按Enter时,它才会移动到下一行。

要么

如果我用这个

cin.get() or cin.get(c)

当我按Enter键时,它将移至下一条指令。

但是我希望它移动到键盘上按下的任何键的下一行,该怎么办?


据我所知,问题是,您的外壳正在等待您按ENTER或EOF,然后让您的程序处理缓冲区中的内容或类似内容。也许拥有更多知识的人可以提供真实的解释。但是我认为这并不像它最初看起来那样容易。
卢卡斯

7
到目前为止,处理此问题的最简单方法也是唯一可移植的方法,是将提示从“按任意键继续”更改为“按Enter键继续”。
rob mayoff 2012年

@Lucas-我将Mac与xcode一起使用。
itsaboutcode '02

@卢卡斯:这不是外壳,而是程序本身。
基思·汤普森

@KeithThompson:这是两年多以前的事,但是我想指出的一点是,输入队列不是由用户进程处理而是由内核处理。
卢卡斯

Answers:


64

在Windows上:

system("pause");

在Mac和Linux上:

system("read");

将输出“按任意键继续...”,显然,等待任何键被按下。我希望那是你的意思


5
system调用外部程序。您无需调用外部程序即可等待任何键!这就像叫别人坐在您的计算机前打开计算机一样-这是很慢的解决方案。
GingerPlusPlus 2014年

8
没错,但只有一行!有时,为节省计算机某些周期而付出的努力是不值得的……
Elliot Cameron

14
放慢速度,它将调用碰巧在PATH中找到的第一个名为“ pause”的程序,并为其赋予调用程序的特权级别。即使您只是测试自己的代码的学生,它仍然是巨大的安全风险。
安妮·奎因

6
@XDfaceme-暂停是system()要求Windows运行的实际程序。但是,如果还有另一个名为“暂停”的程序,并且Windows会首先找到该程序,它将改为运行它。我想我可能已经夸大了意义,但是使用cin.get()并按回车键可以暂停/取消暂停程序仍然更容易
Anne Quinn 2015年

2
我不是绝对的粉丝。大胆地声明system(“ pause”)有安全隐患,即使测试您自己的代码也使情况恶化,这是有害的。cin.get()可能是正确的解决方案,但它不能解决所问的问题... cin.get()仅在按下“ enter”或“ return”之后才响应。具体来说,问题是响应任何按键。system(“ pause”)正是这样做的。首先,我唯一看到它有用的地方是从Visual Studio调试时在dev类中,因此我认为system(“ pause”)可以工作
Gregor

52

如果您使用的是Windows,则可以使用kbhit()Microsoft运行时库中的一部分。如果您使用的是Linux,则可以kbhit这样实现():

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;

  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

  ch = getchar();

  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);

  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }

  return 0;
}

更新:上面的功能在OS X上有效(至少在OS X 10.5.8-Leopard上有效,因此我希望它可以在OS X的最新版本上使用)。该要点可以另存为,kbhit.c并可以在Linux和OS X上使用

gcc -o kbhit kbhit.c

当与

./kbhit

它提示您进行按键操作,并在您击键时退出(不限于Enter键或可打印键)。

@Johnsyweb-请详细说明“详细的规范答案”和“所有问题”的含义。另外,请“跨平台”:通过此实现,kbhit()您可以在Linux / Unix / OS X / Windows上的C ++程序中具有相同的功能-您可能要引用其他平台?

@Johnsyweb的进一步更新: C ++应用程序不存在于密封的C ++环境中。C ++成功的一个重要原因是与C的互操作性。所有主流平台都是使用C接口实现的(即使内部实现使用的是C ++),因此您对“传统”的谈论似乎不合时宜。另外,当我们谈论单个函数时,为什么要为此使用C ++(“带有类的C”)?正如我所指出的,您可以用C ++编写并轻松访问此功能,并且应用程序的用户不太可能关心您如何实现它。


很遗憾,没有标准的方式来处理控制台,您总是不得不使用一些操作系统魔术
Vargas

1
@Johnsyweb:谢谢,但你的例子表明,我沮丧关于C ++特性。您的kbhit()函数terminal_settings在一行中声明了一个变量,该变量似乎什么也没做,但是却可以完成所有工作。有些人可能认为它很整洁,但是我发现它对于无法读取的点是晦涩的。
Vinay Sajip,2012年

1
@VinaySajip:我的实现使用RAII,这是C ++中最重要的惯用法之一。尽管在此示例中并不一定要这么做,但我发现当您要保存一些状态并恢复状态时,这是一种很好的习惯。
Johnsyweb

4
@Johnsyweb:我知道RAII及其带来的好处,我本人是老手C ++。我的观点仍然是:声明与幕后行为之间没有明确的联系。尽管它可能使用的是闪亮的C ++惯用语,而不是普通的旧C,但我的观点是,它几乎不能说明问题,而可以使正在发生的事情变得模糊。这绝不是针对您个人的-只是关于如何使用C ++创建代码的一种见解,这不必要地很难理解。纳夫说,我现在就下车。
Vinay Sajip,2012年

2
一切都很好,但是仍然在标准输入上捕获了以前的所有垃圾:-)
Tiago

8

没有完全可移植的解决方案。

comp.lang.c常见问题的问题19.1在一定程度上涵盖了此问题,提供了适用于Windows,类Unix系统甚至MS-DOS和VMS的解决方案。

快速而不完整的摘要:

  • 您可以使用该curses库;调用cbreak()后跟getch()(不要与Windows特定的getch()功能混淆)。请注意,curses通常控制终端,因此这可能是过大的。
  • 您可能可以ioctl()用来操纵终端设置。
  • 在POSIX兼容的系统,tcgetattr()并且tcsetattr()可能是一个更好的解决方案。
  • 在Unix上,您可以system()用来调用stty命令。
  • 在MS-DOS上,您可以使用getch()getche()
  • 在VMS(现在称为OpenVMS)上,屏幕管理(SMG$)例程可以解决问题。

所有这些C解决方案都应在C ++中同样有效;我不知道任何特定于C ++的解决方案。


所有这些多平台解决方案都可以与某人一起编写,该函数的实现是根据您的平台执行正确的操作的,该平台具有许多预处理程序来确定该平台是什么。
CashCow 2014年

6

要实现此功能,您可以使用ncurses库,该库在Windows和Linux(据我所知都是MacOS)上都已实现。


好吧,这是一种分配,其中我不能使用任何外部库等,因此只需要留在“章节上下文” lox中即可。
itsaboutcode

2
然后,唯一的选择是为您计划支持的每个OS实现所需的功能。
Kirill V. Lyadvinsky 09年

1
ncurses或多或少是VT200客户端。简单地从TTY中读取内容有点过大。
greyfade

5

在Windows中,此简短程序可以实现目标:getch暂停控制台,直到按下某个键为止(https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/

#include<iostream.h>
#include<conio.h>

using namespace std;

void  check()
{
    char chk; int j;
    cout<<"\n\nPress any key to continue...";
    chk=getch();
    j=chk;
    for(int i=1;i<=256;i++)
      if(i==j) break;
    clrscr();
}

void main()
{
    clrscr();
    check();
    cout<<"\n\nIt works!";
    getch();
}

应该注意的是,getch不是标准库的一部分。


1
这个非常简单的程序。它非常容易理解
BoldX

1
请说明getch()cin.get或有什么不同cin>>,也许有些文档链接
user2622016 2015年

1
conio仅在Windows上
Andrew

4

我调查了您要达到的目标,因为我记得我想做同样的事情。受Vinay启发我写了一些对我东西,我对此有些理解。但是我不是专家,所以请小心。

我不知道Vinay是如何知道您正在使用Mac OS X的,但是它在大多数类似unix的OS上都应该像这样工作。资源非常有帮助opengroup.org

使用该功能之前,请确保先冲洗缓冲区。

#include <stdio.h>
#include <termios.h>        //termios, TCSANOW, ECHO, ICANON
#include <unistd.h>     //STDIN_FILENO


void pressKey()
{
    //the struct termios stores all kinds of flags which can manipulate the I/O Interface
    //I have an old one to save the old settings and a new 
    static struct termios oldt, newt;
    printf("Press key to continue....\n");

    //tcgetattr gets the parameters of the current terminal
    //STDIN_FILENO will tell tcgetattr that it should write the settings
    // of stdin to oldt
    tcgetattr( STDIN_FILENO, &oldt);
    //now the settings will be copied 
    newt = oldt;

    //two of the c_lflag will be turned off
    //ECHO which is responsible for displaying the input of the user in the terminal
    //ICANON is the essential one! Normally this takes care that one line at a time will be processed
    //that means it will return if it sees a "\n" or an EOF or an EOL
    newt.c_lflag &= ~(ICANON | ECHO );      

    //Those new settings will be set to STDIN
    //TCSANOW tells tcsetattr to change attributes immediately. 
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    //now the char wil be requested
    getchar();

    //the old settings will be written back to STDIN
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}


int main(void)
{
  pressKey();
  printf("END\n");
  return 0;
}

O_NONBLOCK似乎也很重要,但对我而言并没有任何改变。

如果有更深入的知识的人对此发表评论并提出建议,我将不胜感激。


应该设置O_NONBLOCK,因为没有它,getchar()(调用read())将阻止等待输入。kbhit()解决方案无需等待就可以告诉您是否按下了键;您可以在等待循环中或其他任何目的中使用它,因此它是一种更通用的解决方案。没有O_NONBLOCK的解决方案将一直等到击键为止-可以解决此问题,但通常用处不大。
Vinay Sajip,2009年

4

您可以使用Microsoft特定的函数_getch

#include <iostream>
#include <conio.h>
// ...
// ...
// ...
cout << "Press any key to continue..." << endl;
_getch();
cout << "Something" << endl;

3

这在Windows平台上有效: 它直接使用微处理器寄存器,可用于检查按键或鼠标按钮

    #include<stdio.h>
    #include<conio.h>
    #include<dos.h>
    void main()
    {
     clrscr();
     union REGS in,out;
     in.h.ah=0x00;
     printf("Press any key : ");

     int86(0x16,&in,&out);
     printf("Ascii : %d\n",out.h.al);
     char ch = out.h.al;
     printf("Charcter Pressed : %c",&ch);
     printf("Scan code : %d",out.h.ah);
     getch();
    }

3

如果您使用的是Visual Studio 2012或更早版本,请使用此getch()功能;如果使用的是Visual Studio 2013或更高版本,请使用_getch()。您将不得不使用#include <conio.h>。例:

#include <iostream>
#include <conio.h>

int main()
{
   std::cout << "Press any key to continue. . .\n";
   _getch(); //Or getch()
}

1

您可以使用getchar例程。

从上面的链接:

/* getchar example : typewriter */
#include <stdio.h>

int main ()
{
  char c;
  puts ("Enter text. Include a dot ('.') in a sentence to exit:");
  do {
    c=getchar();
    putchar (c);
  } while (c != '.');
  return 0;
}

3
如果我正确理解他,我认为这不是OP所要的。您仍然必须按ENTER填充缓冲区,然后getchar()才能从缓冲区读取数据。
卢卡斯

0

您也可以使用conio.h中的getch()。像这样:

...includes, defines etc
void main()
{
//operator
getch(); //now this function is waiting for any key press. When you have pressed its     just     //finish and next line of code will be called
}

因此,由于UNIX没有conio.h,我们可以通过以下代码来模拟getch()(但是此代码已经由Vinary编写,我失败了):

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

int mygetch( ) {
  struct termios oldt,
             newt;
  int            ch;
  tcgetattr( STDIN_FILENO, &oldt );
  newt = oldt;
  newt.c_lflag &= ~( ICANON | ECHO );
  tcsetattr( STDIN_FILENO, TCSANOW, &newt );
  ch = getchar();
  tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
  return ch;
}

-1

如果现在在MSDN上查找kbhit()函数,则表明该函数已被弃用。使用_kbhit()代替。

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}

-1
#include <iostream>
using namespace std;

int main () {
    bool boolean;
    boolean = true;

    if (boolean == true) {

        cout << "press any key to continue";
        cin >> boolean;

    }
    return 0;
}

-4

只需使用system("pause");命令。

所有其他答案使问题变得更加复杂。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.