Como simular “Pressione qualquer tecla para continuar?”

87

Estou tentando escrever um programa C ++ no qual, quando o usuário insere qualquer caractere do teclado, ele deve passar para a próxima linha de código.

Aqui está o meu código:

char c;

cin>>c;

cout<<"Something"<<endl;

mas isso não está funcionando, porque ele só vai para a próxima linha quando eu insiro algum caractere e pressiono ENTER.

OU

Se eu usar isso

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

ele se move para a próxima linha de instrução quando eu pressiono Enter.

Mas eu queria ir para a próxima linha em qualquer tecla pressionada no teclado, como isso pode ser feito?

seu código
fonte
Pelo que eu sei, o problema é que seu shell está esperando que você pressione ENTER ou EOF e então deixará seu programa cuidar de tudo o que está no buffer, ou algo assim. Talvez alguém com um pouco mais de conhecimento possa fornecer uma explicação real. Mas acho que não é tão fácil quanto parece à primeira vista.
Lucas
7
De longe, a maneira mais fácil de lidar com isso, e a única maneira portátil, é alterar o prompt de “Pressione qualquer tecla para continuar” para “Pressione a tecla Enter para continuar”.
rob mayoff
@Lucas - Estou usando mac com xcode.
código sobre
@Lucas: Não é o shell, é o próprio programa.
Keith Thompson
@KeithThompson: Isso foi há mais de dois anos, mas acho que estava tentando deixar claro que a fila de entrada não é tratada pelo processo do usuário, mas dentro do kernel.
Lucas

Respostas:

64

No Windows:

system("pause");

e no Mac e Linux:

system("read");

irá imprimir "Pressione qualquer tecla para continuar ..." e, obviamente, espere que qualquer tecla seja pressionada. Espero que seja isso que você quis dizer

MrMartin
fonte
5
systemchama um programa externo. Você não precisa chamar um programa externo para esperar por qualquer tecla! É como chamar outra pessoa para ligar o computador quando você está sentado na frente dele - esta é uma solução lenta.
GingerPlusPlus
8
É verdade, mas tem apenas uma linha! Às vezes simplesmente não vale a pena o esforço para salvar alguns ciclos do computador ...
Elliot Cameron
14
Esqueça a lentidão, ele chama o primeiro programa denominado "pausa" que encontra no PATH e dá a ele o nível de privilégio do programa de chamada. Mesmo se você for um estudante apenas testando seu próprio código, ainda é um grande risco de segurança.
Anne Quinn
6
@XDfaceme - pause é um programa real que system () pede ao Windows para executar. Se, entretanto, houver outro programa chamado 'pause' e o Windows encontrar esse programa primeiro, ele irá executá-lo. Acho que posso ter exagerado um pouco o significado, mas ainda é mais fácil apenas usar cin.get () e clicar em voltar para pausar / retomar um programa
Anne Quinn
2
Não sou fã de absolutos. Fazer uma declaração geral de que o sistema ("pausa") é um risco à segurança e que é ruim mesmo quando testar seu próprio código está deixando as coisas fora de proporção aqui. cin.get () pode ser a solução certa, mas não resolve a questão que estava sendo feita ... cin.get () só responde depois que "enter" ou "return" são pressionados. A questão era especificamente responder a qualquer pressionamento de tecla. sistema ("pausa") faz exatamente isso. Em primeiro lugar, o único lugar onde vi isso como útil é em uma classe dev ao depurar do Visual Studio, então acho que o sistema ("pausa") funciona
Gregor
52

Se você estiver no Windows, pode usar o kbhit()que faz parte da biblioteca de tempo de execução da Microsoft. Se você estiver no Linux, pode implementar o seguinte kbhit( fonte ):

#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;
}

Atualização: A função acima funciona no OS X (pelo menos, no OS X 10.5.8 - Leopard, então eu esperaria que funcionasse nas versões mais recentes do OS X). Esta essência pode ser salva kbhit.ce compilada no Linux e no OS X com

gcc -o kbhit kbhit.c

Quando executado com

./kbhit

Ele solicita que você pressione uma tecla e sai quando você pressiona uma tecla (não se limitando a Enter ou teclas imprimíveis).

@Johnsyweb - explique o que você entende por "resposta canônica detalhada" e "todas as questões". Além disso, re "plataforma cruzada": com esta implementação, kbhit()você pode ter a mesma funcionalidade em um programa C ++ no Linux / Unix / OS X / Windows - a quais outras plataformas você pode estar se referindo?

Atualização adicional para @Johnsyweb: os aplicativos C ++ não residem em um ambiente C ++ hermeticamente fechado. Uma grande razão para o sucesso do C ++ é a interoperabilidade com C. Todas as plataformas principais são implementadas com interfaces C (mesmo se a implementação interna estiver usando C ++), então sua conversa sobre "legado" parece fora do lugar. Além disso, como estamos falando sobre uma única função, por que você precisa do C ++ para isso ("C com classes")? Como mencionei, você pode escrever em C ++ e acessar essa funcionalidade facilmente, e os usuários do seu aplicativo provavelmente não se importarão com a implementação.

Vinay Sajip
fonte
É uma pena que não haja uma maneira padrão de lidar com o console, você sempre tem que usar um pouco de magia do sistema operacional
Vargas
1
@Johnsyweb: Obrigado você , mas o seu exemplo mostra características que me deprimem sobre C ++. Sua kbhit()função declara uma terminal_settingsvariável em uma linha que parece não fazer nada, mas faz tudo. Alguns podem achar que é legal, mas acho que é obscuro ao ponto de ilegível.
Vinay Sajip
1
@VinaySajip: Minha implementação usa RAII , que é uma das expressões idiomáticas mais importantes em C ++. Embora não seja estritamente necessário neste exemplo, descobri que é um bom hábito adquirir quando quiser salvar algum estado e restaurá-lo.
Johnsyweb
4
@Johnsyweb: Conheço RAII e os benefícios que ele traz, também sou um veterano em C ++. Meu ponto permanece: não há conexão clara entre a declaração e o que ela está fazendo nos bastidores. Embora possa estar usando expressões idiomáticas C ++ brilhantes em vez do C simples e antigo, minha opinião é que faz pouco para iluminar e muito para obscurecer o que está acontecendo. Isso não é direcionado a você pessoalmente de forma alguma - é apenas uma opinião sobre como o C ++ pode ser usado para criar código desnecessariamente difícil de entender. Vou sair da minha caixa de sabão agora, 'nuff disse.
Vinay Sajip
2
É bom e tudo, mas ainda pega qualquer lixo anterior no stdin :-)
Tiago
8

Não existe uma solução totalmente portátil.

Pergunta 19.1 do FAQ comp.lang.c cobre isso com alguma profundidade, com soluções para Windows, sistemas semelhantes ao Unix e até mesmo MS-DOS e VMS.

Um resumo rápido e incompleto:

  • Você pode usar a cursesbiblioteca; chamada cbreak()seguida de getch()(não deve ser confundida com a getch()função específica do Windows ). Observe quecurses geralmente assume o controle do terminal, portanto, é provável que seja um exagero.
  • Você pode ser capaz de usar ioctl() para manipular as configurações do terminal.
  • Em sistemas compatíveis com POSIX, tcgetattr()etcsetattr() pode ser uma solução melhor.
  • No Unix, você pode usar system()para invocar ostty comando.
  • No MS-DOS, você pode usar getch()ougetche() .
  • No VMS (agora chamado de OpenVMS), as SMG$rotinas de gerenciamento de tela ( ) podem resolver o problema.

Todas essas soluções C devem funcionar igualmente bem em C ++; Não conheço nenhuma solução específica para C ++.

Keith Thompson
fonte
Todas essas soluções multiplataforma poderiam fazer com que alguém escrevesse uma função que é implementada para fazer a coisa certa dependendo da sua plataforma com muitos pré-processadores para determinar qual é a plataforma.
CashCow de
6

Para obter essa funcionalidade, você pode usar a biblioteca ncurses que foi implementada no Windows e no Linux (e MacOS, até onde eu sei).

Kirill V. Lyadvinsky
fonte
Bem, este é um tipo de tarefa em que eu não posso usar nenhuma biblioteca externa etc, então apenas tenho que ficar no "contexto do capítulo" lox.
código sobre
2
Então, a única opção é implementar a funcionalidade necessária para cada sistema operacional que você planeja oferecer suporte.
Kirill V. Lyadvinsky
1
ncurses é mais ou menos um cliente VT200. Um pouco exagero por simplesmente ler do TTY.
greyfade
5

No Windows, este programa curto atinge o objetivo: getchpausa o console até que uma tecla seja pressionada ( 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();
}

Deve-se observar que getch não faz parte da biblioteca padrão.

BoldX
fonte
1
Este programa muito simples. É muito fácil de entender
BoldX
1
Explique como getch()é diferente de cin.getou cin>>, talvez, algum link para a documentação
usuário 2622016
1
conio está apenas no Windows
Andrew Wolfe
4

Verifiquei o que você está tentando alcançar, porque me lembro que queria fazer a mesma coisa. Inspirado por Vinay , escrevi algo que funciona para mim e meio que entendo. Mas eu não sou um especialista, então tenha cuidado.

Não sei como Vinay sabe que você está usando Mac OS X. Mas deve funcionar mais ou menos assim com a maioria dos sistemas operacionais unix. O opengroup.org é um recurso realmente útil

Certifique-se de limpar o buffer antes de usar a função.

#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 também parece ser um sinalizador importante, mas não mudou nada para mim.

Agradeço se pessoas com algum conhecimento mais profundo comentassem sobre isso e dessem alguns conselhos.

Lucas
fonte
O_NONBLOCK deve ser definido porque sem ele, getchar () (que chama read ()) bloqueará a espera pela entrada. A solução kbhit () informa se você pressiona uma tecla, sem esperar; você pode usá-lo em um loop de espera ou para qualquer outro propósito, portanto, é uma solução mais geral. Uma solução sem O_NONBLOCK irá esperar até que uma tecla seja pressionada - OK para esta pergunta, mas geralmente menos útil.
Vinay Sajip de
4

Você pode usar a função específica da Microsoft _getch :

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

Isso funciona em uma plataforma Windows: Ele usa os registros do microprocessador diretamente e pode ser usado para verificar o pressionamento de tecla ou o botão do mouse

    #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();
    }
Rohit Vipin Mathews
fonte
3

Se você estiver usando o Visual Studio 2012 ou mais antigo, use a getch()função, se estiver usando o Visual Studio 2013 ou mais recente, use _getch(). Você terá que usar #include <conio.h>. Exemplo:

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

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

Você pode usar a rotina getchar .

No link acima:

/* 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;
}
Nick Dandoulakis
fonte
3
Não creio que seja isso que o OP estava pedindo, se entendi bem. Você ainda precisa pressionar ENTER para preencher o buffer antes que getchar () possa ler dele.
Lucas
0

Além disso, você pode usar getch () de conio.h. Bem assim:

...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
}

Então, como o UNIX não tem conio.h, podemos simular getch () por este código (mas este código já foi escrito por Vinary, minha falha):

#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;
}
m9_psy
fonte
-1

Se você consultar a função kbhit () no MSDN agora, verá que a função está obsoleta. Use _kbhit () em vez disso.

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}
BG4WCE
fonte
-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;
}
Jeffery
fonte
-4

Basta usar o system("pause");comando.

Todas as outras respostas complicam o problema.

Chade
fonte