C언어에서 콘솔의 키 입력 받기 (윈도우즈)

이 포스트에서는 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

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다