컴퓨터 공부/© C

[C언어] 8. 문자

letzgorats 2021. 2. 13. 19:43

8. 문자

 

<아스키코드>

 

- C프로그램의 문자는 아스키 코드(Ascii Code)를 따른다

- 아스키 코드는 0부터 127까지 1바이트로 구성되며 주요 문자를 출력하도록 해준다

0: 48~9:57

A: 65~Z: 90 (소문자와 대문자의 차이는 32이다)

a: 97~z:122

숫자,대문자,소문자의 기본적인 아스키코드는 기억하자!!(숫자,대문자,소문자 순으로 아스키코드가 크다)

 

(예제1) 아스키코드값이 어떤 문자를 가질까

#include <stdio.h>

int main(void) {

        char a = 65;

        printf("%c\n",a);

        system("pause");
        return 0;
}

(해설) 변수의 자료형은 char이고 캐릭터형 자체에 숫자를 넣어서(char a=65;) 처리를 할 수 있다. printf문에서는 형식 지정자로 %c를 쓴다. 출력값은 아스키코드가 65이므로 ‘A’가 나온다. 여기서 알 수 있는 것은 문자는 우리 눈에는 문자로 보이지만 사실 컴퓨터가 그러한 문자를 받아들이는 것에 있어서는 숫자로 기록을 한다는 점이다.

 

< getchar() 함수>

 

- getchar()를 이용해서 사용자에게 입력을 받아 출력하는 방식도 있다.

(, getchar()는 단 하나의 문자를 입력받는다.)

 

(예제2) getchar()

#include <stdio.h>

int main(void) {

        char a=getchar();

        printf("%c\n",a);

        system("pause");
        return 0;
}

(해설) 우선 사용자에게 입력을 받는데 #define _CRT_SECURE_NO_WARNINGS 도 안해주고 scanf()도 없는 것이 특징이다. getchar()함수를 이용하면 사용자가 어떠한 하나의 문자를 입력하면 그대로 출력이 되는 함수이다.(그에 따른 아스키코드값이 출력된다고들 하는데, 실제로 코드를 돌려보면 그냥 입력한 것이 그대로 나오되, tsad213saf이런식 입력했으면 맨 앞 글자인t만 나왔다.)

문자열을 넣었을 때는 맨 앞 문자가 그대로 출력되어 나온다. 그렇다면 아스키코드값이 출력되려면 어떻게 해야할까? printf문을 잘 살펴봐라.%c로 되어 있지 않는가? 이거를 %d로 살짝 고쳐주면 그에 따른 아스키코드값을 출력할 수 있게 된다. 실제로 해봤더니 afadsf를 누르니 맨앞글자인 a의 아스키코드값 97이 출력되었다!

 

<버퍼(Buffer)>

 

- 문자열을 처리할 때 버퍼의 개념이 많이 사용된다.

- 임시적으로 특정한 데이터를 저장하기 위한 목적으로 사용된다.

- C프로그램은 사용자가 의도하지 않아도 자동으로 버퍼를 이용해 입출력을 처리한다.

(무슨말이냐! 컴퓨터는 사실 한번에 처리할 수 있는 데이터의 양이 매우 한정적이다. 물론 연산 속도는 빠르지만 일종의 버퍼라는 것에 우리가 처리할 데이터를 담아서 천천히 한 개씩 돌아가면서 처리를 시켜주는 것이다. 그렇게 때문에, 내부적으로는 우리가 아무리 많은 양을 입력 한다고 한들 한번에 처리 되지않고 사실 버퍼에 담겼다가 처리가 된다는 것이다. ▶ 우리가 의도하지 않아도 자동으로 buffer로 입출력을 처리하고 있는 뜻이다!) buffer는 저장공간이야~

 

(예제3) 입력 버퍼로 인해 흔히 발생하는 오류

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main(void) {

        int a;

        char c;

        scanf("%d",&a);

        printf("%d\n",a);

        scanf("%c", &c);

        printf("%c\n", c);

        system("pause");
        return 0;
}

(해설) 사용자에게 차례대로 먼저 정수형 데이터부터 입력을 받고(scanf("%d",&a);) 그 다음 정수형데이터를 출력하도록 한다.(printf("%d\n",a);) 그리고 이어서 하나의 문자 데이터를 받고(scanf("%c", &c);) 그 다음 문자 데이터를 출력하도록 한다.(printf("%c\n", c);) 그런 다음 프로그램을 실행시키면 맨처음에 특정한 숫자를 입력한 이후에 엔터를 치면 그대로 프로그램이 종료되버린다. 왜 그럴까??

이것은 사실 컴퓨터는 줄바꿈이나 공백등도 전부다 아스키코드로 관리를 하고 있는데, 우리가 이렇게 숫자를 입력한 다음 엔터를 치게 되면 그 숫자값은 a에 들어가고 바로 뒤에 들어오는 공백 즉 엔터라는 그 줄바꿈자체를 바로 문자로 인식을 해버려서(엔터키 또한 아스키코드에서 10으로 표현된다) 문자로 입력을 받겠다는 것이다. , a에는 우리가 입력한 숫자가 c에는 공백(줄바꿈)이 들어가게 되는것이다.

-입력 버퍼로 인해 흔히 발생되는 오류! 이런게 발생되지 않게 하기 위해선 어떻게 해야할까??

 

(예제4) 남아 있는 입력 버퍼를 항상 지울 수 있다.(비울 수 있다)

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main(void) {

        int a;char c;

        scanf("%d",&a);

        printf("%d\n",a);

        int temp;

        while ((temp = getchar()) != EOF && temp != '\n') {}  # 바로 여기!

        scanf("%c", &c);
        
        printf("%c\n", c);

        system("pause");
        return 0;
}

(해설 ①)

숫자와 문자를 동시에 입력받는 경우에는 중간에 입력버퍼를 지워주는 내용을 넣어주면 되는것이다. 예전부터 궁금했던 것인데 왜 int main(void){}처럼 int로 자료형을 써줄까? 이제 답을 알 수 있을 거다! 아까 봤듯이 아스키코드가 숫자로 표현되었지? , 모든 문자들은 다 내부적으로는 숫자로 관리가 되기 때문에 이렇게 int temp;처럼 int형을 사용해서도 문자를 받을 수가 있는 것이다!

 

(해설 ②)

int temp;

while ((temp = getchar()) != EOF && temp != '\n') {}

(EOF End Of File로써 파일의 끝을 의미한다.-파일의 끝에 도달해서 더 이상 읽을 내용이 없다라는 뜻.상수로는 -1)

이게 버퍼의 나머지들을 처리해주는 역할을 하는 거라고 알 수 있다.

while문을 이해하기가 조금 힘들 수 있다.

while문이 True가 되면 {} 으로 넘어가서 아무 것도 안하고 다시 while의 조건을 따지게 된다.

 

while문 안에 보면 (temp = getchar()) != EOF 가 있고, temp != '\n' 있다.

두 조건이 모두”(&&) 만족해야 {}로 넘어가는 것이라는 건 알 수 있겠지?

 

공부하면서 적어놓은 것을 그대로 적어보면,

temp = getchar()을 통해 버퍼에서 값을 받고, 만약 temp

'\n'(개행)이 아니라면 (다른 문자라면!) while문이 True가 되어 실행되어진다.

{중괄호}가 비어 있으므로 아무것도 시행을 안하고 다시 while문으로 돌아와 조건을 따지게 되는데,

temp = getchar()을 통해 다음 버퍼를 입력 받고 조건을 확인한다. 이를 반복으로 마지막에 개행문자를 getchar()을 이용해서 받으면 while문이 False가 되면서 빠져나오게 되고 temp에는 항상 마지막엔 개행문자만이 저장되어진다.

 

혹시 이해가 안된다면..그냥 외워라! 쓰면서 temp덕에 Buffer가 모두 사라져서

입력 오류가 나지 않는다.”라고만 알아두면 될 것 같다.

 

(해설③)

여기서 getchar()함수가 등장하는데, getchar()함수를 통해 입력받은 문자가 ‘temp’라는 변수에 할당 된다. 근데 이 temp가 파일의 끝이 아니고 개행 문자(줄바꿈)이 아니면 (&&는 논리and연산자로 두 피연산자가 모두 참일 때만 참이다.) True이기 때문에 {}가 실행된다.

그러니까 eof가 아니면 실행 ! 오키!(입력받은값이 eof아니면 while문 실행해라)=’그리고 \n(개행)이 아니네? 실행! 오키!(입력받은 값이 개행이 아니면 while문 실행해라)=’이라 하면에는 개행도 포함된거자나 eof만 아니면 되니까 도마찬가지고! 결국 다 합친거니까 결론적으로는 항상 입력 버퍼를 비운다!! 라는 뜻이다! 두 자료형을 입력받는 문제에서 프로그램이 잘 실행되게 하려면 이렇게 입력버퍼를 비워주면 그 밑줄에 있는 문자를 입력하라는 scanf문이 제대로 작동할 것이다!

 

(해설④)

while문 해석 : temp에 한 자씩 받아서 파일의 끝이거나 혹은 줄바꿈을 입력 받았다면 그냥 아무것도 처리하지 않도록 {괄호}안을 비워둔 것이다. 남아있는 개행문자를 없는 것처럼 처리를 해줌으로써 입력버퍼를 효과적으로 비울 수가 있다. , 파일의 끝이거나 개행 문자를 만나면 입력을 멈추므로 항상입력 버퍼를 비운다.

 

while ((temp = getchar()) != EOF && temp != '\n') {}

 

이것과 같은 표현은

 

while ((temp= getchar()) ! = ‘\n’&& temp != EOF )

{

Continue;

}

 

참고 : cboard.cprogramming.com/c-programming/164004-problem-understanding-while-c-=getchar-=%5Cn-c-=eof-%3B.html

 

problem in understanding " while( (c =getchar())!='\n' && c !=EOF );"

Our goals are clear, tasks are defined! Let's work, comrades! -- Nikita Khrushchev

cboard.cprogramming.com

「If there is nothing in the loop, you may also write it like this:

While ((temp=getchar()) != ‘\n’&& temp !=EOF);

But I always recommend the first one, as I have seen」

 

-Buffer(버퍼)라는 개념 다시 짚어보자!!!!-

Buffer는 우리가 입력을 할 때, 그 입력된 데이터를 저장해두는 공간이다.

우리가 데이터를 막 입력하다가 실수로 잘못 입력했을 때 지울 수 있어야하는데,

입력하는 족족 데이터가 전송되어진다면 다시 돌리기가 어려울 것이다.

예를 들어, 아이디를 막 입력하다가 한 글자를 잘못 쳤을 때 고치려 했지만 그냥 데이터가 넘어가버리면 굉장히 불편한 상황에 놓이기 때문에 이와 같은 상황을 방지하기 위해 “Buffer”가 있는 것이다.

데이터를 쭉 받아 놓다가 우리가 Enter라는 키를 입력하면 입력이 종료됐다는 것을 알고

Buffer에 저장해 놓았던 데이터를 쭉 전송한다.

하지만 이 편리한 Buffer 덕분에 위 예제 에서 보았듯이 이상한 처리가 되어버리고 말았다.

우리가 Enter를 입력한다는 것 또한 개행문자인 '\n'을 입력하는 것이기 때문에,

문자형 데이터를 받는 c가 이 '\n'을 입력 받아버린다는 것입니다.

 

(정리)

1) C언어에서의 문자는 내부적으로 아스키 코드 구조를 따른다.

2) C언어에서 문자 입출력 내부에는 Buffer(버퍼)가 존재한다.

 

 

<입력버퍼 비우는 법>

: Scanf 사이에 넣어주면된다.

 

1.간단하게는 getchar(); 를 추가하는 걸로도 가능

2. fflush(stdin);
추가
그런데 이건 gcc에서 안돌아간다. ? 당연히 안된다. 표준 fflush함수는 출력 버퍼를 비우는 녀석이다.
VC
에서는 확장해서 사용하므로 동작하지만 gcc는 안된다.


3. tcflush(0, TCIFLUSH); 추가
0
번 디스크립터를 비워라. 그런데 이는 유닉스 계열에서 터미널과 관련된 함수인 듯

4. rewind(stdio);
추가
rewind
함수는 매개변수로 들어온 스트림을 초기화하는데 사용

5.__fpurge(stdin);
이놈도 리눅스에서만 동작하고 표준은 아니다. stdio_ext.h 추가.

6. fgets(string, sizeof(string), stdin);
문자열 입력을 scanf가 아니라 fgets로 받는다.
그러나 입력시 사이즈를 오버하면 똑같은 문제가 발생한다.
그리고 사이즈를 오버하지 않더라도 문자열 끝에 개행문자가 추가된다.
그래서 다음 줄에
string[strlen(string)-1] = '\0';
로 강제로 널문자를 넣어준다.
 
개인적으로 사람들이 제일 많이 사용하는 방식

7. scanf("%*c", c);
%*c
는 입력은 받지만 저장은 안한다. 즉 비어있는 \n를 날려버린다.

※이걸로하니까 바로되네..

 

8. scanf("%c", &c); scanf(" %c", &c);
%c
앞에 공백을 추가하면 white space를 구분자로 인식한다.


입력들이 간단하게 위와 같이 나온다면 위의 방법들 아무거나 사용해도 되지만
복잡한 입력에 입력버퍼에 뭔가 많이 남는다면
버퍼를 비우는 것이 근본적인 해결방법인듯 하다.

상황에 따라 적절히 골라쓰면 될 듯하다..!

반응형

'컴퓨터 공부 > © C' 카테고리의 다른 글

[C언어] 10. 컴퓨터가 변수를 처리하는 방법  (0) 2021.02.14
[C언어] 9. 문자열  (0) 2021.02.13
[C언어] 7. 포인터  (0) 2021.01.28
[C언어] 6. 배열  (0) 2021.01.28
[C언어] 5. 함수  (0) 2021.01.27