วิธีจำลอง "กดปุ่มใด ๆ เพื่อดำเนินการต่อ"


87

ฉันกำลังพยายามเขียนโปรแกรม C ++ ซึ่งเมื่อผู้ใช้ป้อนอักขระใด ๆ จากแป้นพิมพ์และควรย้ายไปที่บรรทัดถัดไปของรหัส

นี่คือรหัสของฉัน:

char c;

cin>>c;

cout<<"Something"<<endl;

แต่มันใช้งานไม่ได้เพราะมันจะเลื่อนไปยังบรรทัดถัดไปเมื่อฉันป้อนอักขระบางตัวแล้วกด ENTER

หรือ

ถ้าฉันใช้สิ่งนี้

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

มันย้ายไปที่บรรทัดคำสั่งถัดไปเมื่อฉันกด Enter

แต่ฉันต้องการให้มันเลื่อนไปยังบรรทัดถัดไปบนแป้นใด ๆ ที่กดบนแป้นพิมพ์จะทำได้อย่างไร?


เท่าที่ฉันรู้ปัญหาคือเชลล์ของคุณกำลังรอให้คุณกด ENTER หรือ EOF จากนั้นจะปล่อยให้โปรแกรมของคุณดูแลสิ่งที่อยู่ในบัฟเฟอร์หรืออะไรทำนองนี้ บางทีคนที่มีความรู้มากกว่านี้อาจให้คำอธิบายที่แท้จริงได้ แต่ฉันคิดว่ามันไม่ง่ายอย่างที่ปรากฏครั้งแรก
Lucas

7
วิธีที่ง่ายที่สุดในการจัดการสิ่งนี้และวิธีเดียวแบบพกพาคือเปลี่ยนข้อความแจ้งจาก“ กดปุ่มใดก็ได้เพื่อดำเนินการต่อ” เป็น“ กดปุ่ม Enter เพื่อดำเนินการต่อ”
rob mayoff

@Lucas - ฉันใช้ mac กับ xcode
itsaboutcode

@ ลูคัส: ไม่ใช่เปลือก แต่เป็นโปรแกรมเอง
Keith Thompson

@KeithThompson: นี่เป็นเวลามากกว่าสองปีแล้ว แต่ฉันคิดว่าฉันพยายามทำให้จุดที่คิวอินพุตไม่ได้รับการจัดการโดยกระบวนการผู้ใช้ แต่อยู่ในเคอร์เนล
Lucas

คำตอบ:


65

บน Windows:

system("pause");

และบน Mac และ Linux:

system("read");

จะแสดงข้อความ "กดปุ่มใด ๆ เพื่อดำเนินการต่อ ... " และให้รอจนกว่าจะกดปุ่มใด ๆ ฉันหวังว่านั่นคือสิ่งที่คุณหมายถึง


5
systemเรียกโปรแกรมภายนอก คุณไม่ต้องเรียกโปรแกรมภายนอกเพื่อรอคีย์ใด ๆ ! มันเหมือนกับการเรียกให้บุคคลอื่นเปิดคอมพิวเตอร์ของคุณเมื่อคุณนั่งอยู่ข้างหน้าซึ่งเป็นวิธีแก้ปัญหาที่ช้า
GingerPlusPlus

8
จริง แต่มันยาวแค่บรรทัดเดียว! บางครั้งก็ไม่คุ้มค่ากับความพยายามที่จะบันทึกคอมพิวเตอร์ในบางรอบ ...
Elliot Cameron

14
อย่าลืมช้ามันเรียกโปรแกรมแรกชื่อ "หยุดชั่วคราว" ซึ่งจะพบใน PATH และให้ระดับสิทธิ์ของโปรแกรมการโทร แม้ว่าคุณจะเป็นนักเรียนเพียงแค่ทดสอบโค้ดของคุณเอง แต่ก็ยังมีความเสี่ยงด้านความปลอดภัยอย่างมาก
Anne Quinn

6
@XDfaceme - หยุดชั่วคราวเป็นโปรแกรมจริงที่ระบบ () ขอให้ windows ทำงาน อย่างไรก็ตามหากมีโปรแกรมอื่นที่เรียกว่า 'หยุดชั่วคราว' และ Windows พบโปรแกรมนั้นก่อนโปรแกรมจะทำงานแทน ฉันคิดว่าฉันอาจจะพูดเกินความสำคัญไปหน่อย แต่ก็ยังง่ายกว่าที่จะใช้ cin.get () และกดปุ่ม return เพื่อหยุดชั่วคราว / ยกเลิกโปรแกรมชั่วคราว
Anne Quinn

2
ฉันไม่ได้เป็นแฟนของแน่นอน การสร้างคำสั่งแบบครอบคลุมว่าระบบ ("หยุดชั่วคราว") เป็นความเสี่ยงด้านความปลอดภัยและการทดสอบโค้ดของคุณเองนั้นไม่ดีแม้ว่าการทดสอบโค้ดของคุณเองจะทำให้สิ่งต่างๆไม่ได้สัดส่วน cin.get () อาจเป็นวิธีแก้ปัญหาที่ถูกต้อง แต่ไม่สามารถแก้ปัญหาที่ถูกถามได้ ... cin.get () ตอบสนองหลังจากกด "enter" หรือ "return" เท่านั้น คำถามเฉพาะคือตอบสนองต่อการกดแป้นใด ๆ ระบบ ("หยุดชั่วคราว") ทำอย่างนั้น ก่อนอื่นสถานที่เดียวที่ฉันเห็นว่ามีประโยชน์คือในคลาส dev เมื่อทำการดีบักจาก Visual Studio ดังนั้นฉันคิดว่าระบบ ("หยุดชั่วคราว") ใช้งานได้
Gregor

53

หากคุณใช้ 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()คุณสามารถมีฟังก์ชันการทำงานเดียวกันในโปรแกรม C ++ บน Linux / Unix / OS X / Windows - แพลตฟอร์มอื่นใดที่คุณอาจอ้างถึง

การอัปเดตเพิ่มเติมสำหรับ @Johnsyweb:แอปพลิเคชัน C ++ ไม่ได้อยู่ในสภาพแวดล้อม C ++ ที่ปิดสนิท เหตุผลสำคัญสำหรับความสำเร็จของ C ++ คือความสามารถในการทำงานร่วมกันกับ C แพลตฟอร์มหลักทั้งหมดได้รับการติดตั้งด้วยอินเทอร์เฟซ C (แม้ว่าการใช้งานภายในจะใช้ C ++ ก็ตาม) ดังนั้นการพูดถึง "ดั้งเดิม" ของคุณจึงดูเหมือนไม่อยู่ นอกจากนี้ในขณะที่เรากำลังพูดถึงฟังก์ชันเดียวทำไมคุณถึงต้องการ C ++ สำหรับสิ่งนี้ ("C พร้อมคลาส") ดังที่ฉันได้ชี้ให้เห็นคุณสามารถเขียนใน C ++ และเข้าถึงฟังก์ชันนี้ได้อย่างง่ายดายและผู้ใช้แอปพลิเคชันของคุณไม่น่าจะสนใจว่าคุณใช้งานอย่างไร


เป็นเรื่องน่าเสียดายที่ไม่มีวิธีมาตรฐานในการจัดการคอนโซลคุณต้องใช้ OS magick เสมอ
Vargas

1
@Johnsyweb: ขอขอบคุณที่คุณแต่แสดงให้เห็นตัวอย่างของคุณมีการกดดันที่ฉันเกี่ยวกับ C ++ kbhit()ฟังก์ชันของคุณประกาศterminal_settingsตัวแปรในบรรทัดซึ่งดูเหมือนว่าจะไม่ทำอะไรเลย แต่มันก็ทำทุกอย่าง บางคนอาจคิดว่ามันเรียบร้อย แต่ฉันพบว่ามันคลุมเครือจนอ่านไม่ออก
Vinay Sajip

1
@ VinaySajip: การใช้งานของฉันใช้RAIIซึ่งเป็นหนึ่งในสำนวนที่สำคัญที่สุดใน C ++ แม้ว่าจะไม่จำเป็นอย่างยิ่งในตัวอย่างนี้ แต่ฉันพบว่ามันเป็นนิสัยที่ดีที่จะเข้ามาเมื่อคุณต้องการบันทึกสถานะและกู้คืน
Johnsyweb

4
@Johnsyweb: ฉันตระหนักถึง RAII และประโยชน์ที่ได้รับฉันเป็นมือ C ++ เก่าด้วยตัวเอง ประเด็นของฉันยังคงอยู่: ไม่มีการเชื่อมต่อที่ชัดเจนระหว่างการประกาศกับสิ่งที่ทำอยู่เบื้องหลัง แม้ว่ามันอาจจะใช้สำนวน C ++ ที่เป็นประกายแทนที่จะเป็น C แบบเก่า ๆ แต่มุมมองของฉันก็คือการให้แสงสว่างเพียงเล็กน้อยและบดบังสิ่งที่เกิดขึ้น สิ่งนี้ไม่ได้มุ่งตรงมาที่คุณเป็นการส่วนตัว แต่อย่างใด แต่เป็นเพียงความเห็นเกี่ยวกับวิธีใช้ C ++ เพื่อสร้างโค้ดซึ่งยากที่จะเข้าใจโดยไม่จำเป็น ฉันจะออกจากกล่องสบู่ของฉันเดี๋ยวนี้ 'nuff กล่าว
Vinay Sajip

2
เป็นเรื่องที่ดีและทั้งหมด แต่ยังคงจับขยะก่อนหน้านี้บน stdin :-)
Tiago

9

ไม่มีโซลูชันแบบพกพาอย่างสมบูรณ์

คำถามที่ 19.1 ของคำถามที่พบบ่อยของcomp.lang.cกล่าวถึงประเด็นนี้ในเชิงลึกโดยมีโซลูชันสำหรับ Windows ระบบที่เหมือน Unix และแม้แต่ MS-DOS และ VMS

สรุปอย่างรวดเร็วและไม่สมบูรณ์:

  • คุณสามารถใช้cursesห้องสมุด โทรcbreak()ตามด้วยgetch()(เพื่อไม่ให้สับสนกับgetch()ฟังก์ชันเฉพาะของ Windows ) โปรดทราบว่าcursesโดยทั่วไปจะใช้การควบคุมของเทอร์มินัลดังนั้นจึงมีแนวโน้มที่จะใช้งานมากเกินไป
  • คุณอาจสามารถใช้ioctl()เพื่อจัดการกับการตั้งค่าเทอร์มินัลได้
  • บนระบบที่รองรับ POSIX tcgetattr()และtcsetattr()อาจเป็นทางออกที่ดีกว่า
  • บน Unix คุณสามารถใช้system()เพื่อเรียกใช้sttyคำสั่ง
  • บน MS-DOS คุณสามารถใช้getch()หรือgetche().
  • บน VMS (ปัจจุบันเรียกว่า OpenVMS SMG$) กิจวัตรการจัดการหน้าจอ ( ) อาจทำเคล็ดลับได้

โซลูชัน C ทั้งหมดนี้ควรทำงานได้ดีใน C ++ ฉันไม่รู้วิธีแก้ปัญหาเฉพาะของ C ++


โซลูชันหลายแพลตฟอร์มเหล่านี้สามารถทำได้โดยมีคนเขียนฟังก์ชันที่ใช้งานเพื่อทำสิ่งที่ถูกต้องโดยขึ้นอยู่กับแพลตฟอร์มของคุณโดยมีตัวประมวลผลล่วงหน้าจำนวนมากเพื่อกำหนดว่าแพลตฟอร์มนั้นคืออะไร
CashCow

6

เพื่อให้บรรลุฟังก์ชันนี้คุณสามารถใช้ไลบรารีncursesซึ่งใช้งานได้ทั้งบน Windows และ Linux (และ MacOS เท่าที่ฉันรู้)


นี่เป็นงานประเภทหนึ่งที่ฉันไม่สามารถใช้ไลบรารีภายนอกใด ๆ ได้ ฯลฯ ดังนั้นเพียงแค่ต้องอยู่ใน "บริบทบท" lox
itsaboutcode

2
จากนั้นทางเลือกเดียวคือใช้ฟังก์ชันที่จำเป็นสำหรับแต่ละระบบปฏิบัติการที่คุณวางแผนจะรองรับ
Kirill V. Lyadvinsky

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

1
conio ใช้งานบน Windows เท่านั้น
Andrew Wolfe

4

ฉันตรวจสอบสิ่งที่คุณพยายามจะบรรลุเพราะฉันจำได้ว่าฉันอยากทำสิ่งเดียวกัน แรงบันดาลใจจากVinayฉันเขียนสิ่งที่เหมาะกับฉันและฉันก็เข้าใจ แต่ฉันไม่ใช่ผู้เชี่ยวชาญดังนั้นโปรดระวัง

ฉันไม่รู้ว่า Vinay รู้ได้อย่างไรว่าคุณใช้ Mac OS X แต่มันควรจะทำงานแบบนี้กับ OS ที่คล้ายกับ Unix มากที่สุด มีประโยชน์มากเนื่องจากทรัพยากรคือ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

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 () จึงจะอ่านได้
Lucas

0

นอกจากนี้คุณสามารถใช้ getch () จาก conio.h เช่นนี้:

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

0

อีกทางเลือกหนึ่งคือการใช้เธรดที่มีตัวชี้ฟังก์ชัน :

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

-1

หากคุณค้นหาฟังก์ชัน kbhit () บน MSDN ตอนนี้แสดงว่าฟังก์ชันนั้นเลิกใช้แล้ว ใช้ _kbhit () แทน

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


-4

เพียงแค่ใช้system("pause");คำสั่ง

คำตอบอื่น ๆ ทั้งหมดทำให้ปัญหาซับซ้อนขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.