Как смоделировать «Нажмите любую клавишу, чтобы продолжить?»

87

Я пытаюсь написать программу на C ++, в которой, когда пользователь вводит любой символ с клавиатуры, он должен переходить к следующей строке кода.

Вот мой код:

char c;

cin>>c;

cout<<"Something"<<endl;

но это не работает, потому что он переходит к следующей строке только тогда, когда я ввожу какой-либо символ и затем нажимаю ENTER.

ИЛИ

Если я использую это

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

он переходит к следующей строке инструкции, когда я нажимаю Enter.

Но я хотел, чтобы он переместился на следующую строку при нажатии любой клавиши на клавиатуре, как это можно сделать?

itsaboutcode
источник
Насколько мне известно, проблема в том, что ваша оболочка ждет, когда вы нажмете ENTER или EOF, а затем позволит вашей программе позаботиться обо всем, что находится в буфере, или что-то в этом роде. Может быть, кто-нибудь более осведомленный сможет дать реальное объяснение. Но я думаю, что это не так просто, как кажется на первый взгляд.
Лукас,
7
Безусловно, самый простой и единственный переносимый способ - изменить подсказку с «Нажмите любую клавишу, чтобы продолжить» на «Нажмите клавишу Enter, чтобы продолжить».
Роб Мэйофф
@Lucas - я использую Mac с xcode.
itsaboutcode
@Lucas: Дело не в оболочке, а в самой программе.
Кейт Томпсон,
@KeithThompson: Это было более двух лет назад, но я думаю, что пытался подчеркнуть, что очередь ввода обрабатывается не пользовательским процессом, а внутри ядра.
Лукас,

Ответы:

64

В Windows:

system("pause");

и на Mac и Linux:

system("read");

выведет «Нажмите любую клавишу для продолжения ...» и, очевидно, дождется нажатия любой клавиши. Надеюсь, ты это имел в виду

Мистер Мартин
источник
5
systemвызывает внешнюю программу. Вам не нужно вызывать внешнюю программу, чтобы ждать нажатой клавиши! Это все равно, что позвонить другому человеку, чтобы он включил ваш компьютер, когда вы сидите перед ним - это медленное решение.
GingerPlusPlus
8
Верно, но это всего одна строка! Иногда просто не стоит прилагать усилия, чтобы сэкономить компьютеру несколько циклов ...
Эллиот Кэмерон
14
Забудьте о медленном, он вызывает первую программу с именем "pause", которую он случайно нашел в PATH, и дает ей уровень привилегий для вызывающей программы. Даже если вы студент, просто тестирующий свой собственный код, это все равно серьезная угроза безопасности.
Энн Куинн
6
@XDfaceme - pause - это настоящая программа, которую system () запрашивает запуск Windows. Однако, если есть другая программа, называемая «пауза», и Windows первой находит эту программу, она запускает ее. Я полагаю, что могла бы немного преувеличить значение, но все же проще просто использовать cin.get () и нажать return, чтобы приостановить / возобновить программу
Энн Куинн
2
Я не фанат абсолюта. Заявление о том, что система («пауза») представляет собой угрозу безопасности и что она плохая, даже при тестировании вашего собственного кода, здесь несоразмерно. cin.get () может быть правильным решением, но он не решает вопрос, который был задан ... cin.get () отвечает только после нажатия "Enter" или "return". В частности, вопрос заключался в том, чтобы ответить на любое нажатие клавиши. system ("пауза") делает именно это. Прежде всего, единственное место, где я видел это как полезное, - это класс разработчика при отладке из 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 с классами»)? Как я уже отмечал, вы можете писать на C ++ и легко получать доступ к этим функциям, и пользователям вашего приложения вряд ли будет важно, как вы его реализовали.

Винай Саджип
источник
Жалко, что нет стандартного способа работы с консолью, вам всегда нужно использовать некоторую магию ОС
Варгас
1
@Johnsyweb: Спасибо вам , но ваш пример показывает особенности , которые угнетают меня о C ++. Ваша kbhit()функция объявляет terminal_settingsпеременную в строке, которая, кажется, ничего не делает, но все же делает все. Некоторые могут подумать, что это изящно, но я считаю, что это неясно до нечитаемости.
Vinay Sajip
1
@VinaySajip: Моя реализация использует RAII , который является одной из самых важных идиом в C ++. Хотя в этом примере это не является строго необходимым, я считаю хорошей привычкой пользоваться, когда вы хотите сохранить какое-то состояние и восстановить его.
Johnsyweb,
4
@Johnsyweb: Я знаю о RAII и преимуществах, которые он приносит, я сам старый специалист по C ++. Моя точка зрения остается неизменной: нет четкой связи между объявлением и тем, что он делает за кулисами. Хотя он может использовать блестящие идиомы C ++, а не простой старый C, я считаю, что он мало что освещает, а много скрывает, что происходит. Это никоим образом не направлено лично на вас - это просто мнение о том, как можно использовать C ++ для создания кода, которое излишне сложно понять. Я сейчас сойду со своей мыльницы, - сказал Нафф.
Vinay Sajip
2
Это хорошо и все такое, но все равно ловит любой предыдущий мусор на стандартном вводе :-)
Тьяго
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$) подпрограммы Screen Management ( ) могут помочь.

Все эти решения C должны одинаково хорошо работать в 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 не входит в стандартную библиотеку.

Жирный X
источник
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;
}
m9_psy
источник
-1

Если вы сейчас посмотрите на функцию kbhit () в MSDN, там будет сказано, что функция устарела. Вместо этого используйте _kbhit ().

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}
BG4WCE
источник
-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");команду.

Все остальные ответы очень усложняют проблему.

Чад
источник