[C] 문자의 입력과 버퍼

방법이있지·2025년 6월 17일

[정글 4-8주차] C언어

목록 보기
15/26
post-thumbnail

아스키 코드

  • 문자 상수는 컴파일 후 0 ~ 127 사이 정수 값인 아스키 코드 값으로 바뀜
  • e.g,. 'A' -> 65, 'a' -> 97
    • 즉 문자 상수 'a'와 정수 97은 같은 데이터
    • 출력할 땐 변환 문자열 (%c, %d)에 따라 출력 형태가 결정됨
printf("%d\n", 'a');    // 97
printf("%d\n", 97);     // 97
printf("%c\n", 'a');    // a
printf("%c\n", 97);     // a

대소문자 간 변환

  • 대문자는 65(A) ~ 90(Z), 소문자는 97(a) ~ 122(z)의 연속된 아스키 코드 값을 가짐
char change(char letter){
    char gap = 'a' - 'A';   // 대소문자의 차이는 32
    if ('a' <= letter && letter <= 'z'){
        // 소문자인 경우
        return letter - gap;
    } else{
        // 대문자인 경우
        return letter + gap;
    }
}

int main(void){
    // 소문자 -> 대문자
    char small = 'l';
    char large = 'G';

    printf("%c를 대문자로 바꾸면 %c\n", small, change(small));
    printf("%c를 소문자로 바꾸면 %c\n", large, change(large));

    // l를 대문자로 바꾸면 L
    // G를 소문자로 바꾸면 g
    return 0;
}
  • 아스키 코드는 0127 사이 범위를 가지므로, 1바이트 크기의 char형 변수에 저장할 수 있음
  • 소문자에서 대문자를 뺀 값의 차이는 32로 일정
    • 따라서 'a' - 'A' (32)를 더하거나 빼면서 대소문자를 변환할 수 있음

문자 입력 함수

scanf 함수

  • scanf로 문자를 입력받을 수 있음
  • 공백(' '), 탭 문자('\t'), 개행 문자('\n')도 입력받을 수 있음
    • 이를 통틀어서 화이트 스페이스라 함
  • 화이트 스페이스는 %d, %lf, %s로 입력받을 땐 데이터를 구분하는 용도로 쓰이지만,
    • e.g., scanf("%d%d") 꼴에서 10 20과 같이 입력
  • %c에서는 실제 문자열로 입력된다는 점을 주의할 것
char ch1, ch2;

scanf("%c%c", &ch1, &ch2);
printf("입력 문자: %c%c, ", ch1, ch2);
printf("입력 문자 아스키코드: [%d][%d]\n", ch1, ch2);
return 0;

// a와 b를 입력하고 Enter
// 입력 문자: ab, 아스키코드: [97][98]

// a와 공백(스페이스바)를 입력하고 Enter
// 입력 문자: a ,  아스키코드: [97][32]
  • 화이트 스페이스를 무시하고 싶으면 %c %c와 같이 각 문자 앞에 공백을 추가
char ch1, ch2;
scanf(" %c %c", &ch1, &ch2);
printf("입력 문자: %c%c, ", ch1, ch2);
printf("입력 문자 아스키코드: [%d][%d]\n", ch1, ch2);
return 0;

// 스페이스바 -> a -> 스페이스바 -> b를 입력하고 Enter
// 입력 문자: ab, 입력 문자 아스키코드: [97][98]
  • scanf는 입력받은 데이터 수를 int형으로 반환
char ch1, ch2;
int num = scanf(" %c %c", &ch1, &ch2);
printf("입력 데이터 수: %d", num);

// [입력] 5 10
// [출력] 입력 데이터 수: 2
char ch1;
int result = scanf("%c", &ch1);
printf("%d", result);
return 0;

// [입력] Ctrl+Z -> Enter -> Ctrl+Z -> Enter
// [출력] -1
  • scanf 사용 중 Ctrl + Z -> Enter -> Ctrl + Z -> Enter을 누르면 -1 반환 (Win11 기준)
  • 입력의 종료를 뜻하며, EOF(End of File)이라고도 명명

fgetc, fputc

  • 문자 전용 입출력 함수
    • stdin은 키보드로 입력, stdout은 모니터로 출력한다는 뜻이라는 것만 알아두기
  • int fgetc(stdin);: 입력한 문자를 int형으로 반환
  • int fputc(int 입력문자, stdout): int형의 입력문자를 출력 및 반환
    • 출력 과정에서 에러 발생 시 -1 반환
int ch;             // 입력 문자 저장 변수
printf("문자를 입력하세요: ");
ch = fgetc(stdin);  // fgetc는 입력한 문자를 반환
printf("입력한 문자: ");
fputc(ch, stdout);  // fputc는 인수로 받은 문자를 출력
fputc('\n', stdout);

// [입력] 문자를 입력하세요: a
// [출력] 입력한 문자: a
  • fgetcchar형이 아니라 int형을 이용해 입력 문자의 아스키코드를 반환
    • Ctrl + Z -> Enter -> Ctrl + Z -> Enter을 누르면 -1 반환
  • fputcint형 외 char형의 입력문자도 출력 가능
    • 실제로 출력되는 건 int 값의 하위 1바이트이기 때문
    • 따라서 char형도 문제 없이 출력 가능

🤔 fgetc, fputcprintf, scanf 대신 쓸 이유가 있나요? 어차피 기능은 같잖아요?

  • scanf는 입력값을 형식에 맞게 해석하는 과정에서, printf는 형식에 맞춰 값을 출력하는 과정에서, 추가 연산 및 내부 함수 호출이 이루어집니다.
  • 하지만 fgetc, fputc는 단순히 한 문자만 입출력하기 때문에 소폭 더 빠르다고 합니다.
  • 또한 scanf는 입력받은 데이터의 수를, fgetc가 입력한 문자열의 아스키코드를 반환한다는 차이가 있습니다.
    • 대신 Ctrl + Z -> Enter -> Ctrl + Z -> Enter을 누르면 -1을 반환하는 건 똑같습니다.

버퍼

문자를 입력하는 경우

  • 프로그램을 실행하는 중에, 운영체제게 자동으로 할당하는 메모리의 저장 공간
  • 키보드로 입력한 데이터는 우선 버퍼에 저장된 후, scanf 함수를 통해 변수에 입력됨
char ch;    // 임시로 문자를 저장

for (int i = 0; i < 4; i++){
    scanf("%c", &ch);   // 버퍼의 문자를 ch에 저장
    printf("%c", ch);   // ch에 저장된 문자를 출력
}

// [입력] abc
// [출력 (아랫 줄은 개행문자로 인한 빈칸)]
// abc
//

  • a 입력하고 Enter, b 입력하고 Enter, c 입력하고 Enter를 한 게 아니라, abc를 한번에 입력하고 Enter를 한 번만 눌렀음에 유의할 것
  • 처음 scanf가 호출되었을 때, 키보드 입력 후 abc\n 전체 문자열이 버퍼에 저장됨
    • 개행 문자 \n까지 버퍼에 저장됨에 유의
    • 이후 첫 번째 문자 a만 변수 ch에 저장됨
  • 두번째 scanf 호출부턴 버퍼의 남은 문자열에서 문자를 가져옴
    • 따라서 추가 키보드 입력이 필요 없음
    • 차례로 b, c를 변수에 저장
  • 이후 버퍼에 남은 데이터가 없는 경우, 키보드에서 추가로 데이터를 입력받아야 함

⚠️ 실제로 각 문자는 버퍼에 아스키 코드(혹은 유니코드)를 비롯한 비트열 형태로 저장됩니다.

숫자를 입력하는 경우

  • scanf가 숫자를 입력받는 경우, 일단 버퍼에 문자열의 형태로 저장됨
    • e.g., 20 -> 문자 '2', '0'이 아스키 코드 형태로 저장됨
  • 이후 실제 연산이 가능한 숫자로 변환되어 변수에 저장됨
  • e.g., scanf("%d")

입력 버퍼 지우기

문제 예시

  • scanf 함수가 여러 번 호출될 때, 같은 버퍼를 사용함
  • 이때 scanf 함수가 버퍼에 남긴 데이터를, 이후 scanf가 잘못 가져갈 가능성이 있음
    • 특히 버퍼에 남은 개행 문자가 이후 입력에 영향을 줄 수 있음
int num, grade;
printf("학번 입력: ");
scanf("%d", &num);
printf("학점 입력: ");
scanf("%c", &grade);
printf("학번: %d, 학점: %c", num, grade);

// [실행결과 (마지막 줄은 개행 문자로 인한 줄바꿈)]
// 학번 입력: 12213
// 학점 입력: 학번: 12213, 학점:
//

  • scanf에서 12213을 입력하고 Enter를 누르면, 버퍼엔 12213\n이 저장됨
  • 이후 12213은 정수로 변환돼 변수 num에 저장되지만, \n은 버퍼에 남아 있음
  • 두번째 scanf 호출 시 버퍼에 남아 있는 '\n'이 변수 grade에 저장됨
    • scanf("%c", ...)'\n' 같은 화이트스페이스도 저장한다는 점을 기억하기
  • 따라서 학점 입력이 이루어지지 않고, grade를 출력할 땐 개행 문자로 인한 줄바꿈만 출력됨

해결 방법

int num, grade;
printf("학번 입력: ");
scanf("%d", &num);
printf("학점 입력: ");
scanf("%c");    // 개행 문자를 제거
scanf("%c", &grade);
printf("학번: %d, 학점: %c", num, grade);

// [실행결과 (마지막 줄은 개행 문자로 인한 줄바꿈)]
// 학번 입력: 12213
// 학점 입력: A
// 학번: 12213, 학점: A

  • 이때 학점을 입력하기 전 scanf 함수를 추가 호출 해, 버퍼에 남아 있는 개행 문자를 제거해야 함
    • scanf 사용 시 저장할 주소를 명시하지 않으면 단순 제거 가능
  • 이후 버퍼가 비게 되므로, 다시 키보드 입력을 시도할 수 있음
while (fgetc(stdin) != '\n'){
    // 안에 아무것도 없어도 됨
}
  • fgetcscanf와 동일하게 버퍼에서 문자를 하나씩 읽음
  • fgetc는 입력된 문자를 반환
  • 즉, \n을 반환할 때까지 함수 실행을 반복하면,
    • 이전 입력에서 남아 있던 모든 데이터를 비울 수 있음

연습문제

키보드로 입력한 단어 중에서 길이가 가장 긴 단어의 길이를 출력합니다. 한 줄에 하나의 단어만 입력하며 Ctrl + Z를 누를 대까지 입력한 단어 중에서 길이가 가장 긴 단어의 길이를 출력합니다. 배열은 사용하지 않고 출력합니다.

  • Ctrl + Z로 종료되면 fgetc(stdin)-1을 반환
    • -1을 반환할 때까지 while문 실행
  • fgetc(stdin)가 입력한 문자를 반환한다는 점을 사용해서
    • '\n'을 반환할 때마다 단어가 끝났으니, 길이 수와 현재 최대 길이를 비교!
#include <stdio.h>

int main(void){
    char letter;        // 현재 문자를 담는 변수
    int length = 0;     // 현재 단어의 길이
    int maxLength = 0;  // 우리가 찾는, 길이가 가장 긴 단어의 길이

    // Ctrl+Z 콤보로 입력 종료할 때까지 문자 입력
    while ((letter = fgetc(stdin)) != -1){
        if (letter == '\n'){
            if (length > maxLength) maxLength = length;
            length = 0;
        } else {
            length++;
        }
    }

    printf("가장 긴 단어의 길이: %d\n", maxLength);
    // 가장 긴 단어의 길이: 10
}
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

0개의 댓글