Làm thế nào để mô phỏng “Nhấn phím bất kỳ để tiếp tục?”


87

Tôi đang cố gắng viết một chương trình C ++ trong đó khi người dùng nhập bất kỳ ký tự nào từ bàn phím và nó sẽ chuyển sang dòng mã tiếp theo.

Đây là mã của tôi:

char c;

cin>>c;

cout<<"Something"<<endl;

nhưng điều này không hoạt động, vì nó chỉ chuyển sang dòng tiếp theo khi tôi nhập một số ký tự và sau đó nhấn ENTER.

HOẶC LÀ

Nếu tôi sử dụng cái này

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

nó chuyển sang dòng hướng dẫn tiếp theo khi tôi nhấn Enter.

Nhưng tôi muốn nó chuyển sang dòng tiếp theo trên bất kỳ phím nào được nhấn trên bàn phím, làm thế nào điều này có thể được thực hiện?


Theo như tôi biết, vấn đề là trình bao của bạn đang đợi bạn nhấn ENTER hoặc EOF và sau đó sẽ để chương trình của bạn xử lý bất cứ thứ gì có trong bộ đệm, hoặc thứ gì đó tương tự như thế này. Có thể ai đó với một số kiến ​​thức hơn có thể cung cấp một lời giải thích thực sự. Nhưng tôi nghĩ nó không dễ dàng như lần đầu xuất hiện.
Lucas

7
Cho đến nay, cách dễ nhất để xử lý điều này và là cách di động duy nhất, là thay đổi lời nhắc của bạn từ “Nhấn phím bất kỳ để tiếp tục” thành “Nhấn phím Enter để tiếp tục”.
cướp mayoff

@Lucas - Tôi đang sử dụng mac với xcode.
mã số của nó vào

@Lucas: Nó không phải là shell, nó là chính chương trình.
Keith Thompson

@KeithThompson: Đây là hơn hai năm trước, nhưng tôi nghĩ rằng tôi đã cố gắng giải thích rằng hàng đợi đầu vào không được xử lý bởi quy trình người dùng mà bên trong Kernel.
Lucas

Câu trả lời:


64

Trên Windows:

system("pause");

và trên Mac và Linux:

system("read");

sẽ xuất ra "Nhấn phím bất kỳ để tiếp tục ..." và hiển nhiên, hãy đợi bất kỳ phím nào được nhấn. Tôi hy vọng đó là ý của bạn


5
systemgọi chương trình bên ngoài. Bạn không cần phải gọi chương trình bên ngoài để đợi bất kỳ phím nào! Nó giống như kêu gọi người khác bật máy tính của bạn khi bạn đang ngồi trước máy tính - đây là giải pháp chậm.
GingerPlusPlus

8
Đúng, nhưng nó chỉ dài một dòng! Đôi khi nó chỉ là không có giá trị nỗ lực để tiết kiệm máy tính một số chu kỳ ...
Elliot Cameron

14
Quên chậm, nó gọi chương trình đầu tiên có tên là "pause" mà nó tình cờ tìm thấy trong PATH và cấp cho nó mức đặc quyền của chương trình đang gọi. Ngay cả khi bạn là sinh viên chỉ kiểm tra mã của riêng bạn, đó vẫn là một rủi ro bảo mật lớn.
Anne Quinn

6
@XDfaceme - pause là một chương trình thực tế mà hệ thống () yêu cầu các cửa sổ chạy. Tuy nhiên, nếu có một chương trình khác được gọi là 'tạm dừng' và windows tìm thấy chương trình đó trước, nó sẽ chạy chương trình đó thay thế. Tôi cho rằng tôi có thể đã bị thổi phồng tầm quan trọng một chút, nhưng nó vẫn còn dễ dàng hơn để chỉ sử dụng cin.get () và hit trở lại tạm dừng / hủy tạm một chương trình
Anne Quinn

2
Tôi không phải là một fan hâm mộ của sự tuyệt đối. Đưa ra một tuyên bố chung chung rằng hệ thống ("tạm dừng") là một rủi ro bảo mật và điều đó thật tồi tệ ngay cả khi việc kiểm tra mã của chính bạn đang khiến mọi thứ trở nên mất cân đối ở đây. cin.get () có thể là giải pháp phù hợp, nhưng nó không giải quyết được câu hỏi đang được đặt ra ... cin.get () chỉ trả lời sau khi nhấn "enter" hoặc "return". Câu hỏi cụ thể là trả lời cho bất kỳ phím bấm nào. system ("tạm dừng") thực hiện chính xác điều đó. Trước hết, là nơi duy nhất mà tôi đã thấy điều này như là hữu ích trong một lớp học dev khi gỡ lỗi từ Visual Studio, vì vậy tôi nghĩ rằng hệ thống ( "pause") hoạt động
Gregor

52

Nếu đang sử dụng Windows, bạn có thể sử dụng kbhit()nó là một phần của thư viện thời gian chạy của Microsoft. Nếu bạn đang sử dụng Linux, bạn có thể triển khai kbhitnhư vậy ( nguồn ):

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

Cập nhật: Chức năng trên hoạt động trên OS X (ít nhất, trên OS X 10.5.8 - Leopard, vì vậy tôi hy vọng nó sẽ hoạt động trên các phiên bản OS X mới hơn). Đây ý chính có thể được lưu lại dưới dạng kbhit.cvà biên soạn trên cả Linux và OS X với

gcc -o kbhit kbhit.c

Khi chạy với

./kbhit

Nó nhắc bạn nhấn phím và thoát ra khi bạn nhấn một phím (không giới hạn ở phím Enter hoặc phím có thể in được).

@Johnsyweb - vui lòng giải thích rõ ý bạn về "câu trả lời kinh điển chi tiết" và "tất cả các mối quan tâm". Ngoài ra, lại "đa nền tảng": Với việc triển khai này, kbhit()bạn có thể có chức năng tương tự trong chương trình C ++ trên Linux / Unix / OS X / Windows - bạn có thể đề cập đến nền tảng nào khác?

Cập nhật thêm cho @Johnsyweb: Các ứng dụng C ++ không sống trong môi trường C ++ kín. Một lý do lớn cho sự thành công của C ++ là khả năng tương tác với C. Tất cả các nền tảng chính thống đều được triển khai với giao diện C (ngay cả khi việc triển khai nội bộ đang sử dụng C ++) nên việc bạn nói về "di sản" có vẻ không đúng chỗ. Ngoài ra, khi chúng ta đang nói về một hàm duy nhất, tại sao bạn cần C ++ cho điều này ("C với các lớp")? Như tôi đã chỉ ra, bạn có thể viết bằng C ++ và truy cập chức năng này một cách dễ dàng và người dùng ứng dụng của bạn có thể không quan tâm đến cách bạn triển khai nó.


Đó là một sự xấu hổ rằng không có cách tiêu chuẩn để xử lý giao diện điều khiển, bạn luôn phải sử dụng một số hệ điều hành phép thuật
Vargas

1
@Johnsyweb: Cảm ơn bạn , nhưng ví dụ của bạn cho thấy các tính năng khiến tôi chán nản về C ++. kbhit()Hàm của bạn khai báo một terminal_settingsbiến trong một dòng dường như không làm gì cả, nhưng nó thực hiện mọi thứ. Một số người có thể nghĩ rằng nó gọn gàng, nhưng tôi thấy nó khó hiểu đến mức không thể đọc được.
Vinay Sajip

1
@VinaySajip: Việc triển khai của tôi sử dụng RAII , một trong những thành ngữ quan trọng nhất trong C ++. Mặc dù nó không hoàn toàn cần thiết trong ví dụ này, nhưng tôi thấy rằng đó là một thói quen tốt nên có khi bạn muốn lưu một số trạng thái và khôi phục nó.
Johnsyweb

4
@Johnsyweb: Tôi biết RAII và những lợi ích mà nó mang lại, bản thân tôi là một tay C ++ cũ. Quan điểm của tôi vẫn là: không có mối liên hệ rõ ràng nào giữa tuyên bố và những gì nó đang làm ở hậu trường. Mặc dù nó có thể đang sử dụng các thành ngữ C ++ sáng bóng chứ không phải là C thuần túy, nhưng quan điểm của tôi là nó không làm sáng tỏ và che khuất nhiều thứ đang diễn ra. Điều này không nhằm vào cá nhân bạn theo bất kỳ cách nào - đó chỉ là một ý kiến ​​về cách C ++ có thể được sử dụng để tạo mã mà khó hiểu một cách không cần thiết. Tôi sẽ lấy hộp xà phòng ra ngay bây giờ, 'nuff nói.
Vinay Sajip 26/02/12

2
Thật tuyệt và tất cả, nhưng vẫn bắt được bất kỳ rác nào trước đó trên stdin :-)
Tiago

8

Không có giải pháp hoàn toàn di động.

Câu hỏi 19.1 của Câu hỏi thường gặp comp.lang.c trình bày sâu về vấn đề này, với các giải pháp cho Windows, các hệ thống giống Unix và thậm chí cả MS-DOS và VMS.

Một bản tóm tắt nhanh chóng và không đầy đủ:

  • Bạn có thể sử dụng cursesthư viện; gọi cbreak()theo sau getch()(đừng nhầm với getch()chức năng dành riêng cho Windows ). Lưu ý rằng cursesthường kiểm soát thiết bị đầu cuối, vì vậy điều này có thể là quá mức cần thiết.
  • Bạn có thể sử dụng ioctl()để thao tác cài đặt đầu cuối.
  • Trên các hệ thống tương thích với POSIX tcgetattr()tcsetattr()có thể là một giải pháp tốt hơn.
  • Trên Unix, bạn có thể sử dụng system()để gọi sttylệnh.
  • Trên MS-DOS, bạn có thể sử dụng getch()hoặc getche().
  • Trên VMS (bây giờ được gọi là OpenVMS), các quy trình Quản lý màn hình ( SMG$) có thể thực hiện thủ thuật.

Tất cả các giải pháp C này sẽ hoạt động tốt như nhau trong C ++; Tôi không biết bất kỳ giải pháp C ++ cụ thể nào.


Tất cả các giải pháp đa nền tảng này có thể thực hiện với việc ai đó viết một hàm được triển khai để thực hiện điều đúng đắn phụ thuộc vào nền tảng của bạn với rất nhiều bộ xử lý trước để xác định nền tảng đó là gì.
CashCow

6

Để đạt được chức năng này, bạn có thể sử dụng thư viện ncurses đã được triển khai trên cả Windows và Linux (và MacOS theo như tôi biết).


Đây là loại bài tập mà tôi không thể sử dụng bất kỳ thư viện bên ngoài nào, v.v., vì vậy chỉ phải ở trong "ngữ cảnh chương" lox.
mã số của nó vào

2
Sau đó, lựa chọn duy nhất là triển khai chức năng cần thiết cho từng hệ điều hành mà bạn định hỗ trợ.
Kirill V. Lyadvinsky 19/09/09

1
ncurses ít nhiều là một ứng dụng khách VT200. Một chút quá mức cần thiết cho việc đọc đơn giản từ TTY.
greyfade 19/09/09

5

Trong windows, chương trình ngắn này hoàn thành mục tiêu: getchtạm dừng bảng điều khiển cho đến khi nhấn một phím ( 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();
}

Cần lưu ý rằng getch không phải là một phần của thư viện chuẩn.


1
Chương trình này rất đơn giản. Nó rất dễ hiểu
BoldX

1
Xin giải thích như thế nào getch()là khác nhau từ cin.gethay cin>>, có thể một số liên kết đến tài liệu
user2622016

1
conio chỉ có trên Windows
Andrew Wolfe

4

Tôi đã xem xét những gì bạn đang cố gắng đạt được, bởi vì tôi nhớ tôi cũng muốn làm điều tương tự. Lấy cảm hứng từ Vinay, tôi đã viết một cái gì đó phù hợp với mình và tôi hiểu. Nhưng tôi không phải là một chuyên gia, vì vậy hãy cẩn thận.

Tôi không biết làm thế nào Vinay biết bạn đang sử dụng Mac OS X. Nhưng nó sẽ hoạt động như thế này với hầu hết các hệ điều hành giống unix. Thực sự hữu ích vì tài nguyên là opengroup.org

Đảm bảo làm sạch bộ đệm trước khi sử dụng chức năng.

#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 dường như cũng là một lá cờ quan trọng, nhưng nó không thay đổi bất cứ điều gì đối với tôi.

Tôi đánh giá cao nếu những người có kiến ​​thức sâu hơn sẽ nhận xét về điều này và đưa ra một số lời khuyên.


O_NONBLOCK nên được đặt vì nếu không có nó, getchar () (gọi hàm read ()) sẽ chặn chờ đợi đầu vào. Giải pháp kbhit () cho biết bạn có một phím được đánh hay không mà không cần chờ đợi; bạn có thể sử dụng nó trong một vòng lặp chờ hoặc cho bất kỳ mục đích nào khác, vì vậy đây là một giải pháp tổng quát hơn. Một giải pháp không có O_NONBLOCK sẽ đợi cho đến khi một phím được nhấn - OK cho câu hỏi này nhưng ít hữu ích hơn.
Vinay Sajip 20/09/09

4

Bạn có thể sử dụng hàm _getch dành riêng cho Microsoft :

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

3

Tính năng này hoạt động trên Nền tảng Windows: Nó sử dụng trực tiếp các đăng ký của Bộ vi xử lý và có thể được sử dụng để kiểm tra phím bấm hoặc nút chuột

    #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

Nếu bạn đang sử dụng Visual Studio 2012 trở lên, hãy sử dụng getch()hàm, nếu bạn đang sử dụng Visual Studio 2013 trở lên, hãy sử dụng _getch(). Bạn sẽ phải sử dụng #include <conio.h>. Thí dụ:

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

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

1

Bạn có thể sử dụng thói quen getchar .

Từ liên kết trên:

/* 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
Tôi không nghĩ đây là những gì OP đã yêu cầu, nếu tôi hiểu đúng về anh ta. Bạn vẫn phải nhấn ENTER để lấp đầy bộ đệm trước khi getchar () có thể đọc từ nó.
Lucas

0

Ngoài ra, bạn có thể sử dụng getch () từ conio.h. Chỉ như thế này:

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

Vì vậy, vì UNIX không có conio.h, chúng ta có thể mô phỏng getch () bằng mã này (nhưng mã này đã được viết bởi Vinary, tôi thất bại):

#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

Nếu bạn tìm kiếm hàm kbhit () trên MSDN ngay bây giờ, nó cho biết hàm không được dùng nữa. Sử dụng _kbhit () để thay thế.

#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

Chỉ cần sử dụng system("pause");lệnh.

Tất cả các câu trả lời khác làm phức tạp thêm vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.