이 포스트에서는 C언어에서 콘솔의 키 입력을 처리하는 방법을 설명합니다.
참고: 이 방법은 윈도우에서만 가능한 방법입니다. 여기에 사용된 <conio.h> 라이브러리는 윈도우만 지원하는 비표준 라이브러리입니다. 따라서 리눅스에서는 다른 방법으로 구현해야 합니다.
윈도우의 경우는 GUI 기반 프로그램이 주류이고 TUI는 옛 DOS 시절의 프로그램이나 리눅스 등에서 주로 볼 수 있습니다. 하지만 윈도우에서도 명령 프롬프트 창을 기반으로 돌아가는 TUI 프로그램이 가끔 있습니다.
또한, 대학이나 학원 등에서 과제로 TUI 기반의 게임을 포트폴리오로 제출하는 경우도 있습니다.
바로 이러한 경우에 사용되는 키 입력을 구현하는 방법입니다.
#include <stdio.h> #include <conio.h> // DOS/Windows only (Non-standard) int main() { char c; while( _kbhit() ) c = _getch(); // Clear kbhit printf("키를 입력하세요: "); c = _getch(); if (c >= 32 && c <= 126) printf("%c", c); printf("\nKey code: %d", c); if ( _kbhit() ) { c = _getch(); printf(" %d", c); } printf("\n"); return 0; }
이 코드는 다음과 같이 실행됩니다.
키를 입력하세요:
여기서 키보드의 숫자 1 키를 누르면,
키를 입력하세요: 1 Key code: 49
이와 같이 됩니다.
먼저 7번 줄에서 입력된 문자의 코드값을 저장할 변수를 선언하고, 9번 줄에서 키눌림을 초기화합니다.
12번 줄에서 키의 입력을 받습니다. 여기서 사용된 함수는 _getch()
입니다. 원래는 getch()
함수가 쓰였지만, 해당 함수는 비권장(deprecated) 함수가 되면서 최근 제공되는 Visual Studio의 C 컴파일러에는 제외되어 있습니다. 그 대신 앞에 언더스코어(_)를 붙인 _getch()
의 사용이 권장됩니다.
13번 줄에서는 입력된 키의 문자를 출력합니다. 12번 줄에서 _getche()
함수를 대신 쓰고 13번 줄을 생략해도 입력된 키의 문자가 출력되지만, 이 경우 지정된 범위(여기서는 0x20 – 0x7e)를 벗어난 문자도 출력됩니다.
그리고 14번 줄에서는 그 문자에 해당하는 코드를 출력합니다.
15-18번 줄은 확장 키 입력을 처리하는 부분입니다. 확장 키(화살표 키 등)를 누르면 먼저 0이나 -32(224)가 들어가고 또 다른 코드가 후속으로 들어가는데, 이 때 16번 줄은 두 번째 키 코드를 출력, 17번 줄에서 해당 코드를 출력합니다.
만약 확장 키로 F1 키를 누르면,
키를 입력하세요: Key code: 0 59
이와 같이 출력됩니다. 이는 F1 키를 누르면 먼저 0이 들어가고, 이어지는 코드로 59가 들어가는 것입니다.
그런데 위와 같이 구현하면, 프로그램은 키 입력을 받을 때까지 처리가 멈춥니다. 실제 프로그램에서는 키 입력을 기다리면서 다른 처리를 해야 하는 경우가 있습니다. 예를 들어 테트리스는 버튼을 누르고 있지 않은 상태에서도 블록이 내려옵니다. 하지만 위와 같은 코드로 구현하면 프로그램은 다른 처리를 못 하고 사용자의 키 입력을 하염없이(?) 기다려야 하므로, 다른 방식으로 구현해야 합니다.
예를 들면,
#include <stdio.h> #include <time.h> #include <conio.h> // DOS/Windows only (Non-standard) int main() { char c; time_t cur_sec; int i = 0; time(&cur_sec); while(time(NULL) == cur_sec) { /* Wait */ } while( _kbhit() ) c = _getch(); // Clear kbhit c = '\0'; printf("시작\n"); time(&cur_sec); while(!c) { if ( time(NULL) != cur_sec ) { time(&cur_sec); i++; printf("%d초\n", i); } if ( _kbhit() ) { c = _getch(); printf("\nKey code: %d", c); if ( _kbhit() ) { c = _getch(); printf(" %d", c); } printf("\n"); } } return 0; }
이 코드처럼 구현합니다. 여기서는 반복문 안에 _kbhit()
함수를 사용하였습니다. 이는 _getch()
함수에 의한 키 입력 처리가 끝난 후 키 입력이 발생하면 참을, 아니면 거짓을 돌려주는 함수입니다.
19번 줄부터 34번 줄까지가 반복 구간인데, 키 입력을 기다리지 않고 반복하는 동안 키 입력이 있었는지만 검사하고 없으면 계속 진행합니다. 키 입력 처리는 앞의 그 코드와 같지만, 여기서는 _getch()
함수에서 입력을 기다리지 않고 그대로 처리합니다. _getch()
함수는 키 입력이 없을 때는 키 입력을 기다리지만, 키 입력이 발생한 상태에서 _getch()
함수가 나오면 해당 키에 해당하는 코드를 바로 돌려줍니다.
이 코드는 다음과 같이 실행됩니다.
시작 1초 2초
기다리고 있으면 1초씩 지날 때마다 지난 시간이 초 단위로 표시됩니다. 여기서 키를 누르면,
시작 1초 2초 3초 4초 Key code: 49
위와 같이 키 처리 후 반복문을 빠져나와 프로그램을 끝내게 됩니다.
추가로, _getwch()
, _getwche()
함수가 있습니다. 이는 키 값을 유니코드 기반으로 돌려주는 함수입니다.
#include <stdio.h> #include <conio.h> // DOS/Windows only (Non-standard) int main() { int c; // OR wint_t c; while( _kbhit() ) c = _getch(); // Clear kbhit printf("키를 입력하세요: "); c = _getwche(); printf("\nKey code: %d\n", c); return 0; }
이 코드는 다음과 같이 실행됩니다.
키를 입력하세요: 가 Key code: 44032