Як імітувати “Натисніть будь-яку клавішу, щоб продовжити?”


87

Я намагаюся написати програму на C ++, в якій, коли користувач вводить будь-який символ із клавіатури, і він повинен перейти до наступного рядка коду.

Ось мій код:

char c;

cin>>c;

cout<<"Something"<<endl;

але це не працює, оскільки воно переходить до наступного рядка лише тоді, коли я ввожу якийсь символ і натискаю ENTER.

АБО

Якщо я цим користуюся

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

він переходить до наступного рядка вказівок, коли я натискаю Enter.

Але я хотів, щоб він перейшов до наступного рядка на будь-якій клавіші, натиснутої на клавіатурі, як це можна зробити?


Наскільки мені відомо, проблема полягає в тому, що ваша оболонка чекає, коли ви натиснете ENTER або EOF, а потім дозволить вашій програмі подбати про те, що є в буфері, або щось подібне. Можливо, хтось із деякими знаннями може дати справжнє пояснення. Але я думаю, що це не так просто, як здається спочатку.
Лукас

7
Безперечно, найпростіший спосіб впоратися з цим, і єдиний портативний спосіб - це змінити своє запит із "Натисніть будь-яку клавішу, щоб продовжити", на "Натисніть клавішу Enter, щоб продовжити".
пограбування Mayoff

@Lucas - Я використовую mac з xcode.
код

@Lucas: Це не оболонка, а сама програма.
Кіт Томпсон,

@KeithThompson: Це було більше двох років тому, але я думаю, що я намагався сказати, що черга введення не обробляється процесом користувача, а всередині ядра.
Лукас

Відповіді:


64

У Windows:

system("pause");

та на Mac та Linux:

system("read");

виведе "Натисніть будь-яку клавішу, щоб продовжити ...", і очевидно, дочекайтеся натискання будь-якої клавіші. Я сподіваюся, це те, що ви мали на увазі


5
systemдзвінки зовнішньої програми. Вам не потрібно викликати зовнішню програму, щоб чекати будь-якої клавіші! Це все одно, що зателефонувати іншій людині увімкнути комп’ютер, коли ви сидите перед ним - це повільне рішення.
GingerPlusPlus

8
Правда, але довжина всього один рядок! Іноді просто не варто докладати зусиль, щоб зберегти комп’ютер на певних циклах ...
Елліот Камерон

14
Забудьте повільно, він викликає першу програму з назвою "пауза", яку випадково знаходить у PATH, і надає їй рівень привілеїв програми, що викликає. Навіть якщо ви студент, який просто тестує власний код, це все одно величезний ризик для безпеки.
Anne Quinn

6
@XDfaceme - pause - це фактична програма, яку система () просить запустити Windows. Якщо, однак, існує інша програма під назвою "пауза", і Windows знаходить цю програму першою, вона замість неї її запустить. Припускаю, що я, можливо, трохи перебільшив значення, але все-таки простіше просто використовувати cin.get () і натиснути кнопку повернення, щоб призупинити / скасувати паузу програми
Енн Квін

2
Я не шанувальник абсолютів. Складно сказати, що система ("пауза") є ризиком для безпеки, і що це погано, навіть коли тестування власного коду не дає такої пропорції. cin.get () може бути правильним рішенням, але це не вирішує питання, яке задавали ... cin.get () відповідає лише після натискання клавіші "enter" або "return". Питання конкретно полягало у відповіді на будь-яку натискання клавіш. system ("пауза") робить саме це. Перш за все, єдине місце, де я вважаю це корисним, - це клас dev при налагодженні з Visual Studio, тому я думаю, що система ("пауза") працює
Грегор,

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()ви можете мати однакову функціональність у програмі C ++ на Linux / Unix / OS X / Windows - на які інші платформи ви можете посилатися?

Подальше оновлення для @Johnsyweb: Програми C ++ не працюють у герметично закритому середовищі C ++. Головною причиною успіху С ++ є взаємодія з C. Всі основні платформи реалізовані з інтерфейсами C (навіть якщо внутрішня реалізація використовує C ++), тому ваші розмови про "застарілий" виглядають недоречними. Плюс, оскільки ми говоримо про одну функцію, навіщо для цього потрібен C ++ ("C з класами")? Як я вже зазначав, ви можете писати на C ++ і легко отримувати доступ до цієї функціональності, і користувачам вашого додатку навряд чи буде все одно, як ви її реалізували.


Шкода, що не існує стандартного способу обробки консолі, завжди потрібно використовувати якусь магію ОС
Варгас

1
@Johnsyweb: Дякую вам , але ваш приклад показує особливості , які пригнічують мене про C ++. Ваша kbhit()функція оголошує terminal_settingsзмінну в рядку, яка, здається, нічого не робить, і все ж вона робить все. Хтось може подумати, що це акуратно, але я вважаю, що це незрозуміло аж до нечитабельності.
Vinay Sajip

1
@VinaySajip: У моїй реалізації використовується RAII , який є однією з найважливіших ідіом в C ++. Хоча в цьому прикладі це не є суворо необхідним, я знайшов гарну звичку заводити, коли хочеш зберегти якийсь стан і відновити його.
Johnsyweb

4
@Johnsyweb: Я обізнаний про RAII та вигоди, які він приносить, я сам старий C ++. Моя думка залишається: немає чіткого зв'язку між декларацією та тим, що вона робить за лаштунками. Незважаючи на те, що він може використовувати блискучі ідіоми C ++, а не звичайний старий C, я вважаю, що він мало що освітлює, а багато для затемнення того, що відбувається. Це ніяк не спрямовано на вас особисто - це лише думка про те, як C ++ можна використовувати для створення коду, який важко зрозуміти. Я зараз зійду зі своєї мильниці, - сказала Нафф.
Vinay Sajip

2
Це приємно і все, але все одно ловить будь-яке попереднє сміття на stdin :-)
Тіаго

8

Повністю портативного рішення не існує.

Питання 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 ++.


Всі ці мультиплатформні рішення можуть бути зроблені з тим, що хтось пише функцію, яка реалізована, щоб робити те, що залежить від вашої платформи, з великою кількістю попередніх процесорів, щоб визначити, що це за платформа.
CashCow

6

Для досягнення цієї функціональності ви можете скористатися бібліотекою ncurses, яка була реалізована як в Windows, так і в Linux (і MacOS, наскільки мені відомо).


Ну це своєрідне завдання, в якому я не можу використовувати жодних зовнішніх бібліотек тощо, тому просто слід залишатися в "контексті розділів" lox.
itsaboutcode

2
Тоді єдиним варіантом є впровадження необхідних функціональних можливостей для кожної ОС, яку ви плануєте підтримувати.
Кирило Васильович Лядвінський

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
Ендрю Вулф

4

Я вивчив, чого ви намагаєтесь досягти, тому що пам’ятаю, я хотів зробити те саме. Натхненний Вінеєм, я написав щось, що мені підходить, і я начебто розумію. Але я не фахівець, тож будьте обережні.

Я не знаю, як Віней знає, що ви використовуєте Mac OS X. Але це має працювати приблизно так, як у більшості 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
Я не думаю, що про це просив ОП, якщо я його правильно розумію. Вам все одно потрібно натиснути ENTER, щоб заповнити буфер, перш ніж getchar () зможе читати з нього.
Лукас

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

-1

Якщо ви зараз шукаєте функцію kbhit () на MSDN, вона говорить, що ця функція застаріла. Замість цього використовуйте _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");командою.

Усі інші відповіді ускладнюють питання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.