딩굴댕굴

[열혈C] 07. Part4 Chapter21 문자와 문자열 처리 함수

by jennysgap

BOX



프로그램에서 문자열을 다루는 것은 상당히 중요하다. 사람이 이해할 수 있는 모든 정보는 문자열로 표현이 되어야 의미를 지니기 때문이다.
스트림, 데이터 입 · 출력의 개념에 대해 정확히 이해하자. 뒤에서 언급하고 있는 파일 입 · 출력에 대한 내용과 연관성을 지닌다.

21장 내용 정리

스트림과 데이터의 전송

스트림(stream)이란 데이터를 이동시킬 수 있는 '다리'의 역할을 하는 것이라고 생각하면 된다.

실행중인 프로그램과 모니터 사이에 다리(출력 스트림)가 놓여 있고, 실행중인 프로그램과 키보드 사이에도 다리(입력 스트림)가 놓여 있다. 

printf 함수와 scanf 함수를 통해서 데이터를 입 · 출력할 수 있는 이유가 바로 여기에 있다.


스트림과 생성과 소멸

파일 입 · 출력과 콘솔(키보드와 모니터를 의미) 입 · 출력 사이에는 차이점이 하나 있다. 파일과의 연결을 의미하는 스트림 생성에는 우리가 직접 관여해야 하지만, 콘솔과의 연결을 의미하는 스트림은 프로그램 실행 시 자동적으로 생성된다는 것이다.


생각해 보자! printf 함수와 scanf 함수를 사용할 때 스트림을 생성하는 코드를 작성한 적이 있는가? 없다. 

콘솔 입 · 출력을 위한 스트림은 프로그램 실행 시 자동으로 생성되고, 프로그램 종료 시 자동으로 소멸된다.

즉, 기본적으로 제공이 되는 스트림이다.

stderr은 에러 출력을 위한 스트림으로 모니터에 데이터를 출력한다는 점에서 stdout과 완전 동일하다.


문자 출력 함수

하나의 문자를 출력할 때 일반적으로 사용하는 함수 : putchar 함수와 fputc 함수

1
2
3
4
5
6
#include <stdio.h>
 
int putchar(int c);
int fputc(int c, FILE* stream);
 
// 오류 발생 시 EOF 
cs


putchar 함수는 함수 호출 시 인자로 전달된 문자를 '표준 출력 스트림(stdout)'으로 출력해주는 함수. 

fputc 함수는 putchar 함수와 같은 기능을 하는 함수인데, 차이점은 "문자를 출력할 스트림을 지정할 수 있다는 특징"이 있다. 즉, putchar 함수는 모니터로만 문자를 출력하지만, fputc 함수는 모니터뿐만 아니라, 파일에도 문자를 출력(저장)할 수 있는 함수이다.


fputc 함수에 대해서 부연 설명을 하면, fputc 함수의 두 번째 인자 stream은 문자를 출력할 스트림을 지정하기 위해서 사용된다. 따라서 이 인자에다가 표준 출력 스트림을 의미하는 'stdout'을 인자로 전달하게 되면 putchar 함수와 완전히 동일한 함수가 된다. fputc 함수는 파일 입 · 출력에서 주로 사용하게 된다.


참고로, 오류 발생 시 반환되는 EOF의 실제 값은 -1이다.


문자 입력 함수

하나의 문자를 입력받을 때 일반적으로 사용하는 함수 : getchar 함수와 fgetc 함수

1
2
3
4
5
6
#include <stdio.h>
 
int getchar(void);
int fgetc(FILE* stream);
 
// 에러가 발생하거나 파일의 끝에 도달하는 경우 EOF 
cs


getchar 함수는 '표준 입력 스트림(stdin)'으로부터 한 문자를 입력받아서 반환해 주는 함수이다.

fgetc 함수도 getchar 함수와 같은 기능을 하는 함수인데, 차이점은 "문자를 입력받을 스트림을 지정할 수 있다는 특징"이 있다. 즉, fgetc 함수를 이용하면 키보드뿐만 아니라 파일로부터도 데이터를 입력받을 수 있다.


예제

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    char ch = 0;
 
    while (ch != 'q')
    {
        ch = getchar();        // fgetc(stdin)와 같다.
        putchar(ch);         // fputc(ch, stdout)와 같다.
    }
    return 0;
}
cs


EOF에 대해서

함수의 선언을 보면, 경우에 따라(오류 발생 혹은 파일의 끝에 도달했을 경우) EOF를 리턴한다고 되어 있다.

fgetc 함수는 하나의 파일을 정해 놓고 계속해서 데이터를 읽다 보면 결국 파일의 끝에 도달하게 된다.

getchar 함수는 키보드로부터 입력을 받을 경우에는 Ctrl+Z 키의 입력을 파일의 끝이라고 정의해 놓았다. (일종의 약속)


예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
int main()
{
    char ch = 0;
 
    while (ch != EOF)
    {
        ch = getchar();
        putchar(ch);
    }
    printf("program 종료 \n");
    return 0;
}
 
/*
# 실행 결과
12
12
34
34
^Z
  program 종료
*/
cs


문자 단위 입 · 출력 함수가 존재하는 이유!

printf 함수와 scanf 함수는 문자 단위 입 · 출력 함수에 비해서 강력한 기능을 지닌다. 때문에 문자 단위 입 · 출력도 가능하지만, 이 함수들이 진정 빛을 발할 때는 형식을 지정해서 출력 및 입력하는 경우이다. 이렇게 강력한 기능을 지니는 만큼 요구되는 메모리 공간도 크고 속도도 느리다.

정말 필요한 기능이 하나의 문자를 출력하는 것이라면 putchar 함수를 사용해야 한다. 


문자열 출력 함수

하나의 문자열을 출력할 때 일반적으로 사용하는 함수 : puts 함수와 fputs 함수

1
2
3
4
5
6
#include <stdio.h>
 
int puts(const char* s);
int fputs(const char* s, FILE* stream);
 
// 에러가 발생 하는 경우 EOF 
cs


puts 함수는 문자열을 표준 출력 스트림(stdout)으로 출력하기 위해 사용하는 함수이다.

차이점 : puts 함수는 문자열을 출력한 다음에 자동으로 줄을 바꿔 주지만, fputs 함수는 자동으로 줄을 바꿔주지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main(void)
{
    fputs("fputs 함수에 의한 출력, ", stdout);
    fputs("I Love Linux ", stdout);
    fputs("\n", stdout);
 
    puts("pust 함수에 의한 출력, ");
    puts("I Love Linux ");
    return 0;
}
/*
# 실행 결과
fputs 함수에 의한 출력, I Love Linux
pust 함수에 의한 출력,
I Love Linux
계속하려면 아무 키나 누르십시오 . . .
*/
cs


문자열 입력 함수

문자열을 입력받을 때 일반적으로 사용하는 함수 : gets 함수와 fgets 함수

1
2
3
4
5
6
#include <stdio.h>
 
char* gets(char* s);
char* fgets(char* s, int n, FILE* stream);
 
// 에러가 발생하거나 파일의 끝에 도달하는 경우 NULL 포인터 
cs


gets 함수의 사용은 자제해야 한다. 사용자가 Enter 키를 누리기 전까지 입력된 문자열을 읽어들이게 되는데, 만약에 미리 할당에 놓은 배열의 크기보다도 큰 길이의 문자열이 들어오게 되는 경우 배열의 Overflow가 발생한다. 이러한 잠재적 위험성 때문에 fgets 함수만을 사용한다.

fgets 함수의 두 번째 인자 n은 입력받을 수 있는 최대 문자열의 길이를 나타낸다. 따라서 문자열을 입력받을 배열의 길이를 인자로 전달하면, 그 이상의 문자를 읽어들이는 일은 발생하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
 
int main(void)
{
    char str[10];
 
    fputs("문자열을 입력 하세요: ", stdout);
    fgets(str, sizeof(str), stdin);
 
    fputs("입력된 문자열: ", stdout);
    fputs(str, stdout);
    fputs("\n", stdout);
 
    return 0;
}
 
/*
# 실행 결과1
문자열을 입력하세요: 1234
입력된 문자열: 1234

# 실행 결과2
문자열을 입력하세요: 12345678910
입력된 문자열: 123456789
*/
cs


fgets 함수는 입력받을 수는 최대 문자열의 길이 n을 초과하는 문자열이 입력되는 경우에는 n-1개까지의 문자만 입력 받고, 마지막에 NULL 문자를 삽입해 준다. 


표준 입 · 출력과 버퍼(Buffer)

우리가 지금까지 사용해 왔던 입 · 출력 함수들을 표준 입 · 출력 함수라 한다. (scanf, printf 함수 / putchar, getchar 함수)

표준 입 · 출력 함수를 사용하는 경우에는 버퍼라는 것을 제공받게 된다는 특징이 있다. 버퍼는 '여분의 임시 메모리 공간' 이라고 생각하면 된다.

키보드를 통해서 입력하는 데이터는 실행중인 프로그램으로 바로 읽혀지는 것이 아니라, 일단은 입력 버퍼에 저장이 된다. 그러고 나서 입력 버퍼에 존재하는 데이터가 프로그램으로 이동하는 것이다. 키보드로부터 데이터를 입력하고 나서 Enter 키를 누르는 시점이 데이터가 버퍼로 들어가는 시점이다.


버퍼링(Buffering)을 하는 이유

버퍼링 : 데이터를 전송 하는데 있어서 중간에 버퍼(여분의 임시 저장소)를 둬서 전송하고자 하는 데이터를 임시 저장해 두는 것.

창고에 물건을 나르는 경우 손으로 하나씩 나르는 것보다는, 리어카라도 빌려서 한꺼번에 많은 양의 물건을 나르는 것이 보다 효율적이다. 같은 이치로 I/O 잡업들을 어느 정도 쌓아 두었다가 처리를 하는 것이 효율적이기 때문에 버퍼링을 하는 것이다. 


버퍼를 비우는 작업을 하는 fflush 함수

fflush 함수는 버퍼를 지우는 작업을 하는 함수이다.

1
2
3
4
5
#include <stdio.h>
 
int fflush(FILE * stream);
 
// 성공 시 0, 실패 시 EOF 
cs

fflush 함수의 인자로 전달되는 스트림의 종류에 따라 의미가 달라진다.

stdin : 입력 버퍼 안에 존재하는 데이터들은 모두 지운다.

stdout : 출력 버퍼 안에 존재하는 데이터들은 목적지를 향해서 출발하게 된다.


예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
void ClearLineFromReadBuffer(void)
{
    while (getchar() != '\n');
}
 
int main(void)
{
    char perID[7];        // 6 + null 문자
    char name[10];
 
    fputs("주민번호 앞 6 자리를 입력하세요 : ", stdout);
    fgets(perID, sizeof(perID), stdin);        
    // ClearLineFromReadBuffer();
    /* 
    null문자를 제외하고 6문자를 읽음
    \n은 입력버퍼에 남아있는 상태에서 23행의 fgets가 남아있는 \n을 읽어 Error가 발생
    입력버퍼에 남아있는 \n 문자를 지우면 해결됨
    */
 
    fputs("이름을 입력하세요 : ", stdout);
    fgets(name, sizeof(name), stdin);
 
    printf("주민번호 앞자리 : %s \n", perID);
    printf("이름 : %s \n", name);
 
    return 0;
}
cs

ClearReadBuffer 함수가 버퍼에 남겨진 \n 을 getchar로 읽어 입력버퍼에 남겨진 문자들을 모두 지우게 됨.




열혈강의 C 프로그래밍
국내도서
저자 : 윤성우
출판 : 프리렉 2003.12.15
상세보기








반응형

블로그의 정보

jennysgap

jennysgap

활동하기