Захватывать символы со стандартного ввода, не дожидаясь нажатия клавиши ввода
Я никогда не могу вспомнить, как я это делаю, потому что это случается так редко для меня. Но в C или C++, как лучше всего читать символ из стандартного ввода, не дожидаясь перевода строки (нажмите ввод).
Также в идеале это не должно отображать вводимый символ на экране. Я просто хочу захватывать нажатия клавиш без влияния на экран консоли.
21 ответ
Это невозможно сделать в чистом C++, потому что это слишком сильно зависит от используемого терминала, который может быть подключен к stdin (обычно это буферизованная строка). Однако вы можете использовать библиотеку для этого:
- Conio доступен с компиляторами Windows. Используйте функцию
_getch()
чтобы дать вам символ, не дожидаясь клавиши ввода. Я не частый разработчик Windows, но я видел, что мои одноклассники просто включаютconio.h
и использовать это. Смотрите conio.h в Википедии. Это спискиgetch
, который объявлен устаревшим в Visual C++. - curses доступны для Linux, совместимые реализации curses доступны и для Windows. Он также имеет
getch
функция. (пытатьсяman getch
просмотреть его справочную страницу). Смотрите Проклятия в Википедии.
Я бы порекомендовал вам использовать curses, если вы стремитесь к кроссплатформенности. Тем не менее, я уверен, что есть функции, которые вы можете использовать для отключения буферизации линии (я полагаю, что это называется "сырой режим", а не "приготовленный режим" (посмотрите man stty
)). Проклятия справились бы с тобой портативно, если я не ошибаюсь.
В Linux (и других unix-подобных системах) это можно сделать следующим образом:
#include <unistd.h>
#include <termios.h>
char getch() {
char buf = 0;
struct termios old = {0};
if (tcgetattr(0, &old) < 0)
perror("tcsetattr()");
old.c_lflag &= ~ICANON;
old.c_lflag &= ~ECHO;
old.c_cc[VMIN] = 1;
old.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &old) < 0)
perror("tcsetattr ICANON");
if (read(0, &buf, 1) < 0)
perror ("read()");
old.c_lflag |= ICANON;
old.c_lflag |= ECHO;
if (tcsetattr(0, TCSADRAIN, &old) < 0)
perror ("tcsetattr ~ICANON");
return (buf);
}
В основном вы должны отключить канонический режим (и режим эха, чтобы подавить эхо).
Я нашел это на другом форуме, пытаясь решить ту же проблему. Я немного изменил это из того, что нашел. Работает отлично. Я использую OS X, поэтому, если вы работаете с Microsoft, вам нужно найти правильную команду system(), чтобы переключиться в сырой и готовый режимы.
#include <iostream>
#include <stdio.h>
using namespace std;
int main() {
// Output prompt
cout << "Press any key to continue..." << endl;
// Set terminal to raw mode
system("stty raw");
// Wait for single character
char input = getchar();
// Echo input:
cout << "--" << input << "--";
// Reset terminal to normal "cooked" mode
system("stty cooked");
// And we're out of here
return 0;
}
conio.h
функции, которые вам нужны:
int getch();
Prototype
int _getch(void);
Description
_getch obtains a character from stdin. Input is unbuffered, and this
routine will return as soon as a character is available without
waiting for a carriage return. The character is not echoed to stdout.
_getch bypasses the normal buffering done by getchar and getc. ungetc
cannot be used with _getch.
Synonym
Function: getch
int kbhit();
Description
Checks if a keyboard key has been pressed but not yet read.
Return Value
Returns a non-zero value if a key was pressed. Otherwise, returns 0.
libconio http://sourceforge.net/projects/libconio
или же
Linux con ++ реализация conio.h http://sourceforge.net/projects/linux-conioh
Если вы находитесь в Windows, вы можете использовать PeekConsoleInput, чтобы обнаружить, есть ли какие-либо входные данные,
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );
затем используйте ReadConsoleInput, чтобы "потреблять" вводимый символ.
PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
ReadConsoleInput(handle, &buffer, 1, &events);
return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0
если честно, это из какого-то старого кода, который у меня есть, так что вам придется немного поиграться с ним.
Крутая вещь, хотя это то, что он читает ввод без запроса чего-либо, поэтому символы не отображаются вообще.
#include <conio.h>
if (kbhit() != 0) {
cout << getch() << endl;
}
Это использует kbhit()
проверить, нажата ли клавиатура и использует ли она getch()
чтобы получить характер, который нажимается.
Я использую kbhit(), чтобы увидеть, присутствует ли char, а затем getchar(), чтобы прочитать данные. В Windows вы можете использовать "conio.h". В Linux вам нужно будет реализовать свой собственный kbhit().
Смотрите код ниже:
// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>
int kbhit(void) {
static bool initflag = false;
static const int STDIN = 0;
if (!initflag) {
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initflag = true;
}
int nbbytes;
ioctl(STDIN, FIONREAD, &nbbytes); // 0 is STDIN
return nbbytes;
}
// main
#include <unistd.h>
int main(int argc, char** argv) {
char c;
//setbuf(stdout, NULL); // Optional: No buffering.
//setbuf(stdin, NULL); // Optional: No buffering.
printf("Press key");
while (!kbhit()) {
printf(".");
fflush(stdout);
sleep(1);
}
c = getchar();
printf("\nChar received:%c\n", c);
printf("Done.\n");
return 0;
}
ncurses предоставляет хороший способ сделать это! Также это мой самый первый пост (который я помню), поэтому любые комментарии приветствуются. Буду признателен за полезные, но все приветствуются!
скомпилировать: g++ -std= C++11 -pthread -lncurses .cpp -o
#include <iostream>
#include <ncurses.h>
#include <future>
char get_keyboard_input();
int main(int argc, char *argv[])
{
initscr();
raw();
noecho();
keypad(stdscr,true);
auto f = std::async(std::launch::async, get_keyboard_input);
while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
{
// do some work
}
endwin();
std::cout << "returned: " << f.get() << std::endl;
return 0;
}
char get_keyboard_input()
{
char input = '0';
while(input != 'q')
{
input = getch();
}
return input;
}
Поскольку предыдущие решения здесь не работают на разных платформах и имеют проблемы со специальными ключами, вот мое решение, которое работает как в Windows, так и в Linux и использует минимум внешних библиотек (
Windows.h
для Windows и
sys/ioctl.h
+
termios.h
для Linux).
Для символов ASCII ( новая строка / табуляция / пробел / возврат / удаление,!"#$%&'()*+,-. / 0-9:;<=>?@AZ[]^_`az{|}~üäÄöÖÜßµ´§°¹³²) возвращаются коды ASCII (положительные числа), а для специальных клавиш (клавиши со стрелками, страница вверх / вниз, pos1 / end, escape, insert) - отрицательные коды виртуальных клавиш Windows (отрицательные числа) возвращаются.
#include <iostream>
#include <string>
#include <thread> // contains <chrono>
using namespace std;
void println(const string& s="") {
cout << s << endl;
}
void sleep(const double t) {
if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));
}
// ASCII codes (key>0): 8 backspace, 9 tab, 10 newline, 27 escape, 127 delete, !"#$%&'()*+,-./0-9:;<=>?@A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²
// control key codes (key<0): -38/-40/-37/-39 top/bottom/left/right arrow, -33/-34 page up/down, -36/-35 pos1/end
// other key codes (key<0): -45 insert, -144 num lock, -20 caps lock, -91 windows key, -93 kontext menu key, -112 to -123 F1 to F12
// not working: ´ (239), ¹ (251), F3 (-114), F9 to F12 (-120 to -123), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int key_press() { // not working: ´ (239), F3 (code -114 identical to Ä), F11 (-122, toggles fullscreen)
KEY_EVENT_RECORD keyevent;
INPUT_RECORD irec;
DWORD events;
while(true) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &irec, 1, &events);
if(irec.EventType==KEY_EVENT&&((KEY_EVENT_RECORD&)irec.Event).bKeyDown) {
keyevent = (KEY_EVENT_RECORD&)irec.Event;
const int ca = (int)keyevent.uChar.AsciiChar;
const int cv = (int)keyevent.wVirtualKeyCode;
const int key = ca==0 ? -cv : ca;
switch(key) {
case -16: continue; // disable Shift
case -17: continue; // disable Ctrl / AltGr
case -18: continue; // disable Alt / AltGr
case -220: continue; // disable first detection of "^" key (not "^" symbol)
case -221: continue; // disable first detection of "`" key (not "`" symbol)
case -191: continue; // disable AltGr + "#"
case -52: continue; // disable AltGr + "4"
case -53: continue; // disable AltGr + "5"
case -54: continue; // disable AltGr + "6"
case -12: continue; // disable num block 5 with num lock deactivated
case 13: return 10; // enter
case -46: return 127; // delete
case -124: return 132; // ä
case -108: return 148; // ö
case -127: return 129; // ü
case -114: return 142; // Ä
case -103: return 153; // Ö
case -102: return 154; // Ü
case -31: return 225; // ß
case -26: return 230; // µ
case -11: return 245; // §
case -8: return 248; // °
case -49: return 251; // ¹
case -3: return 253; // ²
case -4: return 252; // ³
case 0: continue;
case 1: continue; // disable Ctrl + a (selects all text)
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f (opens search)
case 7: continue; // disable Ctrl + g
//case 8: continue; // disable Ctrl + h (ascii for backspace)
//case 9: continue; // disable Ctrl + i (ascii for tab)
case 10: continue; // disable Ctrl + j
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
//case 13: continue; // disable Ctrl + m (breaks console, ascii for new line)
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v (inserts clipboard)
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z
default: return key; // any other ASCII/virtual character
}
}
}
}
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <termios.h>
void terminal_init() {
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON; // turn off line buffering
term.c_lflag &= ~ECHO; // turn off echoing
tcsetattr(0, TCSANOW, &term);
}
void terminal_exit() {
struct termios term;
tcgetattr(0, &term);
term.c_lflag |= ICANON; // turn on line buffering
term.c_lflag |= ECHO; // turn on echoing
tcsetattr(0, TCSANOW, &term);
}
int kbhit() {
int nbbytes;
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
return nbbytes;
}
int key_press() { // not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F9 to F12 (-120 to 123)
while(true) {
terminal_init();
while(!kbhit()) {
fflush(stdout);
sleep(0.01);
}
int key = (int)getchar();
if(key==27||key==194||key==195) { // escape
key = (int)getchar();
if(key==91) { // [ following escape
key = (int)getchar(); // get code of next char after \e[
if(key==49) { // F5-F8
key = 62+(int)getchar(); // 53, 55-57
if(key==115) key++; // F5 code is too low by 1
getchar(); // take in following ~ (126), but discard code
}
} else if(key==79) { // F1-F4
key = 32+(int)getchar(); // 80-83
}
if(key==51||key==53||key==54||key==50) { // delete, page up/down, insert
getchar(); // take in following ~ (126), but discard code
}
key = -key; // use negative numbers for escaped keys
}
terminal_exit();
switch(key) {
case 127: return 8; // backspace
case -51: return 127; // delete
case -164: return 132; // ä
case -182: return 148; // ö
case -188: return 129; // ü
case -132: return 142; // Ä
case -150: return 153; // Ö
case -156: return 154; // Ü
case -159: return 225; // ß
case -181: return 230; // µ
case -167: return 245; // §
case -176: return 248; // °
case -178: return 253; // ²
case -179: return 252; // ³
case -180: return 239; // ´
case -65: return -38; // top arrow
case -66: return -40; // bottom arrow
case -68: return -37; // left arrow
case -67: return -39; // right arrow
case -53: return -33; // page up
case -54: return -34; // page down
case -72: return -36; // pos1
case -70: return -35; // end
case -50: return -45; // insert
case 0: continue;
case 1: continue; // disable Ctrl + a
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f
case 7: continue; // disable Ctrl + g
//case 8: continue; // disable Ctrl + h (ascii for backspace)
//case 9: continue; // disable Ctrl + i (ascii for tab)
//case 10: continue; // disable Ctrl + j (ascii for new line)
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
case 13: continue; // disable Ctrl + m
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z (terminates program)
default: return key; // any other ASCII character
}
}
}
#endif // Windows/Linux
int main(int argc, char* argv[]) {
while(true) {
const int key = key_press();
println("Input is: "+to_string(key)+", \""+(char)key+"\"");
}
return 0;
}
C и C++ имеют очень абстрактное представление о вводе / выводе, и не существует стандартного способа делать то, что вы хотите. Существуют стандартные способы получения символов из стандартного потока ввода, если они есть, и ни один другой язык не определен. Поэтому любой ответ должен быть специфичным для платформы, возможно, в зависимости не только от операционной системы, но и от структуры программного обеспечения.
Здесь есть некоторые разумные предположения, но нет способа ответить на ваш вопрос, не зная, какова ваша целевая среда.
Самое близкое к портативному - это использовать ncurses
библиотека для перевода терминала в режим cbreak. API гигантский; процедуры, которые вы хотите больше всего,
initscr
а такжеendwin
cbreak
а такжеnocbreak
getch
Удачи!
Ниже приведено решение, извлеченное из Expert C Programming: Deep Secrets, которое должно работать на SVr4. Он использует stty и ioctl.
#include <sys/filio.h>
int kbhit()
{
int i;
ioctl(0, FIONREAD, &i);
return i; /* return a count of chars available to read */
}
main()
{
int i = 0;
intc='';
system("stty raw -echo");
printf("enter 'q' to quit \n");
for (;c!='q';i++) {
if (kbhit()) {
c=getchar();
printf("\n got %c, on iteration %d",c, i);
}
}
system("stty cooked echo");
}
У меня работает на windows:
#include <conio.h>
char c = _getch();
Я всегда хотел, чтобы цикл считывал мой ввод без нажатия клавиши возврата. это сработало для меня.
#include<stdio.h>
main()
{
char ch;
system("stty raw");//seting the terminal in raw mode
while(1)
{
ch=getchar();
if(ch=='~'){ //terminate or come out of raw mode on "~" pressed
system("stty cooked");
//while(1);//you may still run the code
exit(0); //or terminate
}
printf("you pressed %c\n ",ch); //write rest code here
}
}
Вы можете сделать это мобильно, используя SDL (Simple DirectMedia Library), хотя я подозреваю, что вам может не понравиться его поведение. Когда я попробовал это, мне нужно было, чтобы SDL создал новое видеоокно (хотя оно мне и не понадобилось для моей программы), и чтобы это окно "захватывало" почти весь ввод с клавиатуры и мыши (что было хорошо для моего использования, но могло быть раздражающим или неработающим в других ситуациях). Я подозреваю, что это излишне и не стоит, если полная мобильность не является обязательной - в противном случае попробуйте одно из других предложенных решений.
Кстати, это даст вам нажатие клавиш и отдельно отпустит события, если вы в этом заинтересованы.
Я столкнулся с этой же проблемой. Вот небольшое решение для консоли Windows с использованием cygwin g++ с
if(GetKeyState(keycode) & bitANDcompare){};
.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
void clear() {
COORD topLeft = { 0, 0 };
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screen;
DWORD written;
GetConsoleScreenBufferInfo(console, &screen);
FillConsoleOutputCharacterA(
console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
FillConsoleOutputAttribute(
console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
SetConsoleCursorPosition(console, topLeft);
}
class Keyclick{
private:
int key;
char id;
public:
bool keydown = false;
Keyclick(int key1, char id1){
key=key1;
id=id1;
};
void watch(){
if(keydown==false){
if(GetKeyState(key) & 0x8000 ){
cout << id;
cout << " pressed.\r\n";
keydown = true;
}
}
if(keydown == true){
if(!(GetKeyState(key) & 0x8000)) {
cout << "released!!!!!!!!!!\r\n";
keydown = false;
clear();
}
}
};
};
int main()
{
bool primaryloop =true;
Keyclick keysp(VK_SPACE,'S');
Keyclick keyw(0x57,'w');
Keyclick keya(0x41,'a');
Keyclick keys(0x53,'s');
Keyclick keyd(0x44,'d');
Keyclick keyesc(VK_ESCAPE,'E');
while(primaryloop){
keysp.watch();
keyw.watch();
keya.watch();
keys.watch();
keyd.watch();
keyesc.watch();
if(keyesc.keydown){
primaryloop=false;
};
}
return 0;
}
https://github.com/wark77/windows_console_keypoller/blob/main/getkeystate_SOLUTION01.cpp
Также в идеале он не будет отображать вводимый символ на экране. Я просто хочу фиксировать нажатия клавиш, не влияя на экран консоли.
Для этого в Linux вы можете использовать следующую команду bash:
read -sn1 c && printf "You Pressed: %s\n" "$c"
См. мой ответ здесь для получения подробной информации об этом: .
Итак, чтобы сделать это на C или C++ в Linux, вам просто нужно вызвать вышеуказанную команду bash через системный вызов через канал с и
fgets()
так что вы можете прочитать вывод команды bash.
Вот полный пример, который отлично работает в Linux как на C, так и на C++:
read_keypress_system_call.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <stdlib.h>
#define BUFSIZE 32
// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
// 1. https://stackoverflow.com/a/28971647/4561887
// 2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return -1;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
printf("\nFailed to read cmd response.\n");
return -2;
}
// See meaning of this return value here:
// https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
int retval2 = pclose(fp);
if (retval2 == -1)
{
printf("\nError obtaining the cmd's exit status code.\n");
return -3;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return -4;
}
char keyPressed = buf[0];
return keyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any key to continue: ");
fflush(stdout);
char keyPressed = getKeypress();
if (keyPressed > 0)
{
printf("\nKey pressed = %c\n", keyPressed);
}
return 0;
}
Команды компиляции и запуска C и C++ являются частью вывода ниже. Вот несколько демонстраций:
В С:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P
ИЛИ, в С++:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u
Смотрите также:
- Чтобы сделать еще один шаг вперед, см. мой другой ответ здесь, где я обнаруживаю и анализирую 3 символа за раз, чтобы обнаруживать нажатия клавиш со стрелками Up, Down, Leftили Right: Нажатия клавиш чтения в C ex. Клавиши со стрелками, клавиша ввода
Использованная литература:
- Как я научился читать из канала, чтобы получить вывод системного вызова:
- Как я могу запустить внешнюю программу из C и проанализировать ее вывод?
- Есть ли способ получить вывод команды linux (например, ifconfig) в файле .txt с помощью программы C? [дубликат]
- Как составить и использовать
popen()
в C: использовать-std=gnu17
вместо-std=c17
: popen объявлен неявно, хотя добавлен #include &lt;stdio.h&gt;
- [мой ответ] Как читать без блокировки через bash: сценарий оболочки отвечает на нажатие клавишискрипт оболочки отвечает на нажатие клавиши
- [мой ответ] Как мне прочитать клавишу Enter в качестве ввода в C?
Все 3 вопроса и ответа по этой теме
Вот версия, которая не распространяется на систему (написана и протестирована на macOS 10.14)
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
char* getStr( char* buffer , int maxRead ) {
int numRead = 0;
char ch;
struct termios old = {0};
struct termios new = {0};
if( tcgetattr( 0 , &old ) < 0 ) perror( "tcgetattr() old settings" );
if( tcgetattr( 0 , &new ) < 0 ) perror( "tcgetaart() new settings" );
cfmakeraw( &new );
if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );
for( int i = 0 ; i < maxRead ; i++) {
ch = getchar();
switch( ch ) {
case EOF:
case '\n':
case '\r':
goto exit_getStr;
break;
default:
printf( "%1c" , ch );
buffer[ numRead++ ] = ch;
if( numRead >= maxRead ) {
goto exit_getStr;
}
break;
}
}
exit_getStr:
if( tcsetattr( 0 , TCSADRAIN , &old) < 0) perror ("tcsetattr reset to old" );
printf( "\n" );
return buffer;
}
int main( void )
{
const int maxChars = 20;
char stringBuffer[ maxChars+1 ];
memset( stringBuffer , 0 , maxChars+1 ); // initialize to 0
printf( "enter a string: ");
getStr( stringBuffer , maxChars );
printf( "you entered: [%s]\n" , stringBuffer );
}
Вариант ответа ssinfod для Linux, на мой вкус немного чище, реализованный для
wcout
а также
wchar_t
, и стирает недопустимые символы без ошибок.
#include <functional>
//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
#include <sys/ioctl.h> //For FIONREAD.
#include <termios.h>
//Call this at program start to setup for kbhit.
void initTerminalInput()
{
//Disable internal buffering.
std::wcout << std::unitbuf;
//Turn off line buffering.
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
//Returns 0 if there's no input character to read.
int kbhit()
{
static int nbbytes;
ioctl(0, FIONREAD, &nbbytes);
return nbbytes;
}
#endif
//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
static wchar_t inputWChar;
do
{
//Wait until there's an input character.
while (!kbhit())
{
}
inputWChar = getwchar();
//Validate the input character.
if (validationFunction(inputWChar))
{
//Valid.
break;
}
else
{
//Erase the invalid character.
std::wcout << L"\b \b";
}
} while (true);
return inputWChar;
}
В приведенном ниже примере я хотел, чтобы пользователь ввел 1, 2 или 3. Любые другие введенные символы не будут отображаться, и он будет ждать, пока не будет нажат один из допустимых символов:
int main()
{
#ifdef __unix__
initTerminalInput();
#endif
getWChar([] (wchar_t inputWChar)
{
return (inputWChar >= L'1' && inputWChar <= L'3');
});
return 0;
}
В WINDOWS делаю так:
#include <Windows.h>
int main()
{
system("PAUSE>NUL");
if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
//whatever I want to do after 'A' is pressed
}
}
Здесь можно найти список ключей со своим шестнадцатеричным значением для каждого:https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes