Bagaimana cara mensimulasikan "Tekan sembarang tombol untuk melanjutkan?"

87

Saya mencoba menulis program C ++ di mana ketika pengguna memasukkan karakter apa pun dari keyboard dan itu harus pindah ke baris kode berikutnya.

Ini kode saya:

char c;

cin>>c;

cout<<"Something"<<endl;

tetapi ini tidak berhasil, karena hanya pindah ke baris berikutnya ketika saya memasukkan beberapa karakter lalu menekan ENTER.

ATAU

Jika saya menggunakan ini

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

itu pindah ke baris instruksi berikutnya ketika saya menekan Enter.

Tapi saya ingin pindah ke baris berikutnya pada sembarang tombol yang ditekan di keyboard, bagaimana ini bisa dilakukan?

itsaboutcode
sumber
Sejauh yang saya tahu, masalahnya adalah, bahwa shell Anda menunggu Anda untuk menekan ENTER atau EOF dan kemudian akan membiarkan program Anda mengurus apa pun yang ada di buffer, atau sesuatu seperti ini. Mungkin seseorang dengan lebih banyak pengetahuan bisa memberikan penjelasan yang nyata. Tapi menurut saya ini tidak semudah yang pertama kali muncul.
Lucas
7
Sejauh ini cara termudah untuk menangani ini, dan satu-satunya cara portabel, adalah dengan mengubah prompt Anda dari "Tekan sembarang tombol untuk melanjutkan" ke "Tekan tombol Enter untuk melanjutkan".
merampok mayoff
@ Lucas - Saya menggunakan mac dengan xcode.
itsaboutcode
@ Lucas: Ini bukan shell, ini adalah program itu sendiri.
Keith Thompson
@KeithThompson: Ini terjadi lebih dari dua tahun yang lalu, tetapi saya rasa saya mencoba untuk menunjukkan bahwa antrian input tidak ditangani oleh proses pengguna tetapi di dalam Kernel.
Lucas

Jawaban:

65

Di Windows:

system("pause");

dan di Mac dan Linux:

system("read");

akan menampilkan "Tekan sembarang tombol untuk melanjutkan ..." dan tentu saja, tunggu tombol apa saja untuk ditekan. Saya harap itu yang Anda maksud

MrMartin
sumber
5
systemmemanggil program eksternal. Anda tidak perlu memanggil program eksternal untuk menunggu tombol apa pun! Ini seperti memanggil orang lain untuk menyalakan komputer Anda ketika Anda duduk di depannya - ini adalah solusi yang lambat.
GingerPlusPlus
8
Benar, tapi panjangnya hanya satu baris! Terkadang tidak sepadan dengan usaha untuk menyelamatkan komputer beberapa siklus ...
Elliot Cameron
14
Lupakan lambat, ia memanggil program pertama bernama "jeda" yang kebetulan ditemukan di PATH, dan memberinya tingkat hak istimewa program pemanggil. Meskipun Anda seorang pelajar yang baru saja menguji kode Anda sendiri, ini masih merupakan risiko keamanan yang sangat besar.
Anne Quinn
6
@XDfaceme - pause adalah program aktual yang diminta system () windows untuk dijalankan. Namun, jika ada program lain yang disebut 'pause' dan windows menemukan program itu terlebih dahulu, itu akan menjalankannya. Saya kira saya mungkin telah melebih-lebihkan signifikansinya sedikit, tetapi masih lebih mudah untuk hanya menggunakan cin.get () dan menekan tombol kembali untuk menjeda / membatalkan jeda program
Anne Quinn
2
Saya bukan penggemar yang absolut. Membuat pernyataan menyeluruh bahwa sistem ("jeda") adalah risiko keamanan dan itu buruk bahkan ketika menguji kode Anda sendiri membuat hal-hal menjadi tidak proporsional di sini. cin.get () mungkin merupakan solusi yang tepat, tetapi tidak menyelesaikan pertanyaan yang ditanyakan ... cin.get () hanya merespons setelah "enter" atau "return" ditekan. Pertanyaannya secara khusus adalah menanggapi setiap penekanan tombol. system ("pause") melakukan hal itu. Pertama-tama, satu-satunya tempat di mana saya melihat ini berguna adalah di kelas dev saat debugging dari Visual Studio, jadi saya pikir sistem itu ("jeda") berfungsi
Gregor
53

Jika Anda menggunakan Windows, Anda dapat menggunakan kbhit()yang merupakan bagian dari pustaka run-time Microsoft. Jika Anda menggunakan Linux, Anda dapat mengimplementasikan sebagai kbhitberikut ( sumber ):

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

Pembaruan: Fungsi di atas berfungsi di OS X (setidaknya, di OS X 10.5.8 - Leopard, jadi saya berharap ini berfungsi pada versi OS X yang lebih baru). Inti ini dapat disimpan sebagai kbhit.cdan dikompilasi di Linux dan OS X dengan

gcc -o kbhit kbhit.c

Saat dijalankan dengan

./kbhit

Ini meminta Anda untuk menekan tombol, dan keluar saat Anda menekan tombol (tidak terbatas pada Enter atau tombol yang dapat dicetak).

@Johnsyweb - uraikan apa yang Anda maksud dengan "jawaban kanonis terperinci" dan "semua masalah". Juga, "lintas-platform": Dengan implementasi ini, kbhit()Anda dapat memiliki fungsi yang sama dalam program C ++ di Linux / Unix / OS X / Windows - platform lain mana yang mungkin Anda rujuk?

Pembaruan lebih lanjut untuk @Johnsyweb: Aplikasi C ++ tidak tinggal di lingkungan C ++ yang tertutup rapat. Alasan utama keberhasilan C ++ adalah interoperabilitas dengan C. Semua platform utama diimplementasikan dengan antarmuka C (meskipun implementasi internal menggunakan C ++) sehingga pembicaraan Anda tentang "warisan" tampaknya tidak pada tempatnya. Plus, karena kita berbicara tentang satu fungsi, mengapa Anda membutuhkan C ++ untuk ini ("C dengan kelas")? Seperti yang saya tunjukkan, Anda dapat menulis dalam C ++ dan mengakses fungsionalitas ini dengan mudah, dan pengguna aplikasi Anda tidak peduli bagaimana Anda menerapkannya.

Vinay Sajip
sumber
Sayang sekali tidak ada cara standar untuk menangani konsol, Anda harus selalu menggunakan beberapa OS magick
Vargas
1
@Johnsyweb: Terima Anda , tetapi contoh Anda menunjukkan fitur yang menekan saya tentang C ++. kbhit()Fungsi Anda mendeklarasikan terminal_settingsvariabel dalam satu baris yang tampaknya tidak melakukan apa-apa, namun melakukan segalanya. Beberapa orang mungkin berpikir itu rapi, tetapi saya merasa tidak jelas sampai tidak terbaca.
Vinay Sajip
1
@VinaySajip: Implementasi saya menggunakan RAII , yang merupakan salah satu idiom terpenting dalam C ++. Meskipun tidak sepenuhnya diperlukan dalam contoh ini, menurut saya kebiasaan yang baik untuk dilakukan saat Anda ingin menyelamatkan suatu keadaan dan memulihkannya.
Johnsyweb
4
@Johnsyweb: Saya sadar akan RAII dan manfaat yang dibawanya, saya sendiri adalah seorang C ++ tua. Poin saya tetap: tidak ada hubungan yang jelas antara deklarasi tersebut dan apa yang dilakukannya di balik layar. Meskipun mungkin menggunakan idiom C ++ mengkilap daripada C lama biasa, pandangan saya adalah bahwa itu tidak banyak menerangi dan banyak mengaburkan apa yang sedang terjadi. Ini tidak ditujukan kepada Anda secara pribadi dengan cara apa pun - ini hanya pendapat tentang bagaimana C ++ dapat digunakan untuk membuat kode yang sulit dipahami. Saya akan keluar dari kotak sabun saya sekarang, 'kata nuff.
Vinay Sajip
2
Ini bagus dan semuanya, tetapi masih menangkap sampah sebelumnya di stdin :-)
Tiago
9

Tidak ada solusi yang sepenuhnya portabel.

Pertanyaan 19.1 di FAQ comp.lang.c membahas hal ini secara mendalam, dengan solusi untuk Windows, sistem mirip Unix, dan bahkan MS-DOS dan VMS.

Ringkasan cepat dan tidak lengkap:

  • Anda bisa menggunakan cursesperpustakaan; panggilan cbreak()diikuti oleh getch()(jangan bingung dengan getch()fungsi khusus Windows ). Perhatikan bahwa cursesumumnya mengambil kendali terminal, jadi ini mungkin berlebihan.
  • Anda mungkin dapat menggunakan ioctl()untuk memanipulasi pengaturan terminal.
  • Pada sistem yang sesuai dengan POSIX, tcgetattr()dan tcsetattr()mungkin merupakan solusi yang lebih baik.
  • Di Unix, Anda dapat menggunakan system()untuk menjalankan sttyperintah.
  • Di MS-DOS, Anda dapat menggunakan getch()atau getche().
  • Pada VMS (sekarang disebut OpenVMS), SMG$rutinitas Manajemen Layar ( ) mungkin berhasil.

Semua solusi C ini harus bekerja sama baiknya di C ++; Saya tidak tahu ada solusi khusus C ++.

Keith Thompson
sumber
Semua solusi multi-platform ini dapat dilakukan dengan seseorang yang menulis fungsi yang diimplementasikan untuk melakukan hal yang benar bergantung pada platform Anda dengan banyak praprosesor untuk menentukan platform apa itu.
CashCow
6

Untuk mencapai fungsionalitas ini, Anda dapat menggunakan pustaka ncurses yang diimplementasikan pada Windows dan Linux (dan MacOS sejauh yang saya tahu).

Kirill V. Lyadvinsky
sumber
Ini adalah jenis tugas di mana saya tidak bisa menggunakan perpustakaan eksternal dll, jadi hanya harus tetap dalam "konteks bab" lox.
itsaboutcode
2
Maka satu-satunya pilihan adalah mengimplementasikan fungsionalitas yang diperlukan untuk setiap OS yang ingin Anda dukung.
Kirill V. Lyadvinsky
1
ncurses lebih atau kurang merupakan klien VT200. Sedikit berlebihan untuk sekadar membaca dari TTY.
greyfade
5

Di windows, program singkat ini mencapai tujuan: getchmenjeda konsol sampai tombol ditekan ( 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();
}

Perlu dicatat bahwa getch bukan bagian dari pustaka standar.

BoldX
sumber
1
Program yang sangat sederhana ini. Sangat mudah untuk dipahami
BoldX
1
Jelaskan bagaimana getch()perbedaannya dari cin.getatau cin>>, mungkin beberapa tautan ke dokumentasi
user2622016
1
conio hanya ada di Windows
Andrew Wolfe
4

Saya mencari tahu apa yang ingin Anda capai, karena saya ingat saya ingin melakukan hal yang sama. Terinspirasi oleh Vinay, saya menulis sesuatu yang berhasil untuk saya dan saya agak mengerti. Tapi saya bukan ahli, jadi harap berhati-hati.

Saya tidak tahu bagaimana Vinay tahu Anda menggunakan Mac OS X. Tapi seharusnya bekerja seperti ini dengan kebanyakan OS yang mirip unix. Sangat membantu karena sumber dayanya adalah opengroup.org

Pastikan untuk membersihkan buffer sebelum menggunakan fungsi tersebut.

#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 sepertinya juga merupakan flag yang penting, tapi tidak mengubah apapun untuk saya.

Saya menghargai jika orang-orang dengan pengetahuan yang lebih dalam akan mengomentari ini dan memberikan beberapa nasihat.

Lucas
sumber
O_NONBLOCK harus disetel karena tanpanya, getchar () (yang memanggil read ()) akan memblokir menunggu input. Solusi kbhit () memberitahu jika Anda sebuah kunci dipukul, tanpa menunggu; Anda dapat menggunakannya dalam loop tunggu atau untuk tujuan lain, jadi ini adalah solusi yang lebih umum. Sebuah solusi tanpa O_NONBLOCK akan menunggu sampai tombol ditekan - OK untuk pertanyaan ini tetapi kurang bermanfaat secara umum.
Vinay Sajip
4

Anda dapat menggunakan fungsi khusus Microsoft _getch :

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

Ini bekerja pada Platform Windows: Menggunakan register Mikroprosesor secara langsung dan dapat digunakan untuk memeriksa penekanan tombol atau tombol 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
sumber
3

Jika Anda menggunakan Visual Studio 2012 atau yang lebih lama, gunakan getch()fungsi tersebut, jika Anda menggunakan Visual Studio 2013 atau yang lebih baru, gunakan _getch(). Anda harus menggunakan #include <conio.h>. Contoh:

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

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

Anda bisa menggunakan rutinitas getchar .

Dari tautan di atas:

/* 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
sumber
3
Saya tidak berpikir ini yang diminta OP, jika saya memahaminya dengan benar. Anda masih harus menekan ENTER untuk mengisi buffer sebelum getchar () dapat membacanya.
Lucas
0

Anda juga bisa menggunakan getch () dari conio.h. Seperti ini:

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

Jadi, karena UNIX tidak memiliki conio.h, kita dapat mensimulasikan getch () dengan kode ini (tetapi kode ini sudah ditulis oleh Vinary, my fail):

#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
sumber
0

Opsi lainnya adalah menggunakan utas dengan penunjuk fungsi :

#include <iostream>
#include <thread> // this_thread::sleep_for() and thread objects
#include <chrono> // chrono::milliseconds()

bool stopWaitingFlag = false;

void delayms(int millisecondsToSleep) 
{
  std::this_thread::sleep_for(std::chrono::milliseconds(millisecondsToSleep));
}

void WaitForKey() 
{
  while (stopWaitingFlag == false)
  {
    std::cout<<"Display from the thread function"<<std::endl;
    delayms(1000);
  }
}

 

int main()
{
  std::thread threadObj(&WaitForKey);

  char userInput = '\0';
  while (userInput != 'y')
  {
    std::cout << "\e[1;31mWaiting for a key, Enter 'y' for yes\e[0m" << std::endl;
    std::cin >> userInput;
    if (userInput == 'y') {
      stopWaitingFlag = true;
    }
  }
  if (threadObj.joinable())
    threadObj.join();

  std::cout<<"Exit of Main function"<<std::endl;
  return 0;
}
wgaonar.dll
sumber
-1

Jika Anda mencari fungsi kbhit () di MSDN sekarang, dikatakan bahwa fungsi tersebut tidak digunakan lagi. Gunakan _kbhit () sebagai gantinya.

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

Cukup gunakan system("pause");perintah.

Semua jawaban lain memperumit masalah.

Chad
sumber