Enter를 누르지 않고 표준 입력에서 문자를 캡처합니다.


174

나는 종종 그렇게하지 않기 때문에 내가 이것을 어떻게하는지 기억할 수 없다. 그러나 C 또는 C ++에서 줄 바꿈을 기다리지 않고 표준 입력에서 문자를 읽는 가장 좋은 방법은 무엇입니까?

또한 입력 문자를 화면에 에코하지 않는 것이 이상적입니다. 콘솔 화면에 영향을 미치지 않고 키 입력을 캡처하고 싶습니다.


1
@adam-당신은 명확히 할 수 있습니다 : 문자를 사용할 수 없거나 즉시 단일 키 입력을 기다리는 함수를 원하십니까?
Roddy

2
@ Roddy-항상 단일 키 입력을 기다리는 기능을 원합니다.
Adam

답변:


99

순수한 C ++에서는 이식 가능한 방식으로 사용할 수 없습니다. 연결 된 터미널에 너무 많이 의존하기 때문에 stdin(일반적으로 라인 버퍼링 됨). 그러나이를 위해 라이브러리를 사용할 수 있습니다.

  1. conio는 Windows 컴파일러에서 사용 가능합니다. 이 _getch()기능을 사용하여 Enter 키를 기다리지 않고 문자를 제공하십시오. 나는 빈번한 Windows 개발자는 아니지만, 반 친구들이 그것을 포함 <conio.h>하고 사용하는 것을 보았습니다 . conio.hWikipedia를 참조하십시오 . 그것은 나열 getch()Visual C ++에서 사용되지 선언된다.

  2. Linux에서 사용할 수있는 저주. Windows에서도 호환되는 curses 구현을 사용할 수 있습니다. getch()기능 도 있습니다. ( man getch맨 페이지를 보려고 시도하십시오 ). Wikipedia의 저주 를 참조하십시오 .

크로스 플랫폼 호환성을 목표로하는 경우 curses를 사용하는 것이 좋습니다. 즉, 라인 버퍼링을 해제하는 데 사용할 수있는 기능이 있다고 확신 man stty합니다. 내가 실수하지 않으면 저주는 휴대용 방식으로 당신을 위해 그것을 처리 할 것입니다.


7
요즘 ncurses은의 권장 변형입니다 curses.
기금 모니카의 소송

4
도서관이 필요하다면 어떻게 만들었습니까? 라이브러리를 반드시 프로그래밍해야 했으므로 누구나 프로그래밍 할 수 있으므로 필요한 라이브러리 코드를 프로그래밍 할 수없는 이유는 무엇입니까? 그것은 또한 순수한 C ++에서 이식성이 잘못되었을 가능성이 있다
OverloadedCore

@ kid8 이해가 안 돼요
Johannes Schaub-litb

1
@ JohannesSchaub-litb 그는 아마도 라이브러리 구현자가 불가능하다고 말했을 때 어떻게 휴대용 c / c ++에서 이식 가능한 메소드를 만들 었는지 말하려고합니다.
Abhinav Gauniyal

@AbhinavGauniyal 아, 알겠습니다. 그러나 나는 라이브러리 구현자가 이식 가능한 메소드를 만들지 않았다고 말하지 않았다 (즉, 합리적으로 휴대용 프로그램에서 사용되도록). 나는 그들이 휴대용 C ++을 사용하지 않았다는 것을 암시했다. 그들이 분명히하지 않은 것처럼, 나는 그의 의견이 왜 내 대답 의이 부분이 잘못되었다고 말했는지 이해하지 못한다.
Johannes Schaub-litb

81

Linux (및 기타 유닉스 계열 시스템)에서는 다음과 같은 방법으로 수행 할 수 있습니다.

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

기본적으로 표준 모드 (및 에코를 억제하려면 에코 모드)를 꺼야합니다.


이 코드를 구현하려고했지만에 대한 호출에서 오류가 발생했습니다 read. 두 헤더를 모두 포함했습니다.
Michael Dorst

6
read ()는 unistd.h에 정의 된 POSIX 시스템 호출이므로이 코드가 잘못 되었기 때문일 수 있습니다. stdio.h에 우연히 포함시킬 수도 있지만 실제로는이 코드에 stdio.h가 필요하지 않습니다. unistd.h로 바꾸면 좋을 것입니다.
Falcon Momot

1
Raspberry Pi의 ROS 터미널에서 키보드 입력을 얻으려는 동안 어떻게 여기 있는지 알 수 없습니다. 이 코드 스 니펫은 저에게 효과적입니다.
aknay

2
@FalconMomot 내 NetBeans IDE 8.1 (Kali Linux에서)에서는 error: ‘perror’ was not declared in this scope컴파일 할 때 다음 stdio.h과 같이 포함되어 있으면 제대로 작동합니다 unistd.h.
Abhishek Kashyap 22시 37 분

3
이것을 차단하지 않도록 확장하는 쉬운 방법이 있습니까?
Joe Strout

18

동일한 문제를 해결하는 동안 다른 포럼에서 이것을 발견했습니다. 내가 찾은 것에서 약간 수정했습니다. 잘 작동합니다. 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; 
}

13
이것이 효과가 있지만 그만한 가치가 있기 때문에 시스템에 셸을 넣는 것이 내 생각에 가장 좋은 방법은 아닙니다. stty 프로그램은 C로 작성되므로 <termios.h> 또는 <sgtty.h>를 포함하고 외부 프로그램 / 포크 / 무엇에 관계없이 stty가 사용하는 것과 동일한 코드를 호출 할 수 있습니다.
Chris Lutz가

1
나는 개념의 무작위 증거와 혼란을 위해 이것을 필요로했다. 내가 필요한 것만 감사합니다. 주의해야합니다 : 프로그램의 끝에 stty 요리를 넣을 것입니다. 그렇지 않으면 쉘이 stty raw 모드로 유지됩니다. 기본적으로 프로그램이 중지 된 후 쉘 롤이 끊어졌습니다.
Benjamin

당신이 잊었다 고 생각합니다#include <stdlib.h>
NerdOfCode

14

코니 오

필요한 기능은 다음과 같습니다.

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

또는

conio.h의 Linux C ++ 구현 http://sourceforge.net/projects/linux-conioh


9

창문에 있다면 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

솔직히 말하면 이것은 내가 가지고있는 오래된 코드에서 온 것이므로 약간만 사용해야합니다.

멋진 점은 아무 것도 묻지 않고 입력을 읽으므로 문자가 전혀 표시되지 않는다는 것입니다.


9
#include <conio.h>

if (kbhit() != 0) {
    cout << getch() << endl;
}

이 용도는 kbhit()키보드를 누를 사용되는 경우 확인 getch()누르고있는 캐릭터를 얻을 수 있습니다.


5
conio.h ? "conio.h는 텍스트 사용자 인터페이스를 만들기 위해 이전 MS-DOS 컴파일러에서 사용되는 C 헤더 파일입니다." 다소 구식 인 것 같습니다.
kay-SE는 악하다


5

C 및 C ++은 I / O에 대한 매우 추상적 인 관점을 취하며 원하는 것을 수행하는 표준 방법은 없습니다. 표준 입력 스트림에서 문자를 가져 오는 표준 방법이 있으며 가져올 언어가 있으면 어느 언어로도 정의 된 것이 없습니다. 따라서 모든 운영 체제뿐만 아니라 소프트웨어 프레임 워크에 따라 플랫폼별로 응답해야합니다.

여기에는 몇 가지 합리적인 추측이 있지만 대상 환경이 무엇인지 모른 채 질문에 대답 할 방법이 없습니다.


5

kbhit ()을 사용하여 char가 있는지 확인한 다음 getchar ()를 사용하여 데이터를 읽습니다. Windows에서는 "conio.h"를 사용할 수 있습니다. 리눅스에서는 자신의 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;
}

4

휴대용에 가장 가까운 것은 ncurses라이브러리를 사용 하여 터미널을 "파단 모드"로 만드는 것입니다. API는 거대합니다. 가장 원하는 루틴은

  • initscrendwin
  • cbreaknocbreak
  • getch

행운을 빕니다!


3

다음은 Expert C Programming : Deep Secrets 에서 추출한 솔루션 이며 SVr4에서 작동합니다. sttyioctl을 사용합니다 .

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

3

나는 항상 리턴 키를 누르지 않고 입력을 읽는 루프를 원했습니다. 이것은 나를 위해 일했습니다.

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

    }

1
RAW 모드에 들어가면 프로세스를 종료하기가 어렵습니다. "~"를 눌렀을 때와 같이 프로세스를 종료시킬 지점을 유지하십시오. ................ 그렇지 않으면 KILL을 사용하여 다른 터미널에서 프로세스를 종료 할 수 있습니다.
Setu Gupta

3

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


1

SDL (Simple DirectMedia Library)을 사용하여 이식 가능하지만 동작이 마음에 들지 않을 수도 있습니다. 내가 그것을 시도했을 때, 나는 SDL이 새로운 비디오 창을 만들도록해야했다. 다른 상황에서는 성가 시거나 사용할 수 없습니다. 완전한 이식성이 필수가 아닌 한 과잉이며 가치가 없다고 생각합니다. 그렇지 않으면 다른 제안 된 솔루션 중 하나를 사용해보십시오.

그건 그렇고, 당신이 그것에 있다면 키 프레스 및 릴리스 이벤트를 개별적으로 줄 것입니다.


1

다음은 시스템에 셸되지 않는 버전입니다 (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 );
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.