본문 바로가기

Daylogs/C

C: 표준 입출력, 문자와 문자열 처리 함수

이전 글: 6. 함수 포인터와 void 포인터에서 계속



입출력 장치와 스트림

입출력 장치 = 키보드, 마우스, 파일!!!, 카메라, 저장장치 등등..
스트림 = 한 방향으로 흐르는 데이터의 흐름 = 데이터의 이동수단
     운영체제에서 제공하는, 소프트웨어로 구현되어 있는, 가상의 다리(브릿지)
     운영체제는 외부 장치와 프로그램과의 데이터 송수신의 도구가 되는 스트림을 제공하고 있다.

콘솔 입출력을 위한 '입력 스트림'과 '출력 스트림'은 프로그램 시작과 동시에 자동으로 생성된다.
프로그램이 종료되면 자동으로 소멸된다. 이들은 표준 스트림이라 한다.
 * stdin 표준 입력 스트림 (키보드)
 * stdout 표준 출력 스트림(모니터)
 * stderr 표준 에러 스트림(모니터)



표준 입출력과 버퍼

표준 입출력 함수는 ANSI C 표준에서 정의된 함수이다.
이 표준 입출력 함수를 통해 데이터를 입출력하는 경우, 해당 데이터는 운영체제가 제공하는 '메모리 버퍼'를 통과한다.
'메모리 버퍼'는 데이터를 효율적으로 전달하기 위해 임시로 저장하는 공간이다.
예를 들어, 키보드를 통해 입력된 데이터는, 일단 입력 버퍼에 저장된 다음에, (버퍼링 된 후에)
프로그램에서 읽혀진다. 키보드로 입력된 데이터가, 입력 스트림을 거쳐 입력 버퍼로 들어가는 시점은,
엔터키를 눌렀을 때이다.


버퍼링을 하는 이유는?
'데이터 전송의 효율성'과 관련이 있다.
외부 장치와의 데이터 입출력은 생각보다 시간이 오래 걸리는 작업이기 때문이다.
데이터를 한데 묶어서 이동시키는 것이 보다 효율적이다.
창고에 물건을 나를 때 손수레를 써서 가득 채워 나르는 것이 더 빠른 것을 떠올려보라!


출력버퍼를 비우려면?
  int fflush(FILE * stream); // 출력 버퍼에 저장된 데이터가 버퍼를 떠나 목적지로 이동하도록 한다.
                                        // 즉, 표준 출력 버퍼를 비워라!
성공 시 0, 실패 시 EOF


입력버퍼를 비우려면?
  입력버퍼의 비워짐은 데이터의 소멸을 의미하므로 출력버퍼와는 다르다.
  while (getchar() != '\n'); 와 같은 식으로 남은 문자를 다 '읽어들이면' 지워지는 것과 같다.
  이를 함수화해서 활용하라.

  void ClearLineFromReadBuffer(void)
  {
    while(getchar() != '\n');
  }




문자 입출력 함수

문자 출력 함수: putchar, fputc
  int putchar(int c) : 인자로 전달된 문자 정보를 stdout으로 출력한다.
  int fputc(int c, FILE * stream) : 스트림을 선택해 문자를 전송할 수 있다.
호출 성공 시 쓰여진 문자 정보가, 실패 시 EOF가 반환됨.


문자 입력 함수: getchar, fgetc
  int getchar(void) : 표준입력으로 부터 문자를 입력받는다
  int fgetc(FILE * stream) : 선택한 스트림으로부터 문자를 입력받는다.
파일의 끝에 도달하거나 실패 시 EOF 반환

(getchar 에서 문장을 입력하면, 문장이 종료될 때까지 기다렸다가, 문장의 모든 문자를 대상으로 함수를 실행한다)
(printf/scanf 는 서식 기능이 있으므로 더 많은 메모리를 할당하고 느리다)



EOF 는 '파일의 끝'을 의미한다.
즉, '파일의 끝에 도달해서 더 이상 읽을 내용이 없다'는 의미를 전달하는 것이다.
표준 입력의 경우, 즉, 키보드 입력의 경우 '파일의 끝'이 있을까?
없기 때문에 운영체제에서는 EOF를 의미하는 키를 할당해두었다.
윈도우의 경우 Ctrl+Z, 유닉스의 경우 Ctrl+D가 EOF를 의미한다.

반환형이 char가 아니고 int인 이유:
  컴파일러에 따라 char를 unsigned로 구현하는 경우가 있는데 EOF는 -1로 정의된 상수이기 때문.



문자열 입출력 함수
  
문자열 출력함수
  int puts(const char * s); // 표준출력으로 문자열을 출력하고 개행한다.
  int fputs(const char * s, FILE * stream); // 선택한 스트림으로 출력한다. (개행하지 않는다.)
성공한 경우 0보다 큰 값을, 실패 시 EOF 반환


문자열 입력함수
  char * gets(char * s); // 문자열을 입력받는다. 할당한 배열의 크기보다 넘어가지 않게 저장해야 한다.
  char * fgets(char * s, int len, FILE * stream);
     // 선택한 스트림에서 문자열을 입력받는데,
     // len - 1 까지만 자른다. (남은 한 칸은 널 문자 입력을 위한 공간) 
파일에 도달하거나 함수 호출 실패 시 NULL 포인터를 반환한다.

fgets는 개행 문자(\n)을 만날 때까지 계속 읽어들이고,
한 번 입력 후 반복문을 통해 마지막까지 계속 읽어올 수 있다. 


기타 문자열 함수

문자열 길이
size_t strlen(const * s); // 문자열의 길이를 size_t(unsigned int와 동일)로 반환한다.


문자열 복사
  char * strcpy(char * dest, const char * src);
  char * strncpy(char * dest, const char * src, size_t n); // n개까지의 문자만 복사
북사된 문자열의 주소값 반환

주의:  strncpy로 배열 크기를 넘어서지 않게 가져와도, '널 문자를 삽입하진 않는다'
따라서, 목적 배열의 크기보다 하나 작게 복사한 후, 마지막 값에 널 문자를 할당해 줘야한다.

strncpy(dest, src, dest_len - 1);
dest[dest_len-1] = 0;


문자열 덧붙임
  char * strcat(char * dest, const char * src);
  char * strncat(char * dest, const char * src, size_t n); // n개 문자까지만 덧붙인다.
덧붙여지는 문자는 널문자의 위치부터 덧붙여지며,
strncat은 마지막에 강제로 널문자를 추가한다.
따라서 n개를 덧붙이도록 하면, n개 + 널문자.가 덧붙여진다.


문자열 비교
  int strcmp(const char * str1, const char str2); // 문자열의 내용을 비교한다.
  int strncmp(const char * str1, const char str2, size_t n); // n개까지 비교한다.
str1이 클 경우 0보다 큰 값, str2가 클 경우 0보다 작은 값, 같을 경우 0을 리턴한다.
크고 작음은 아스키 코드 값을 기준으로 한다.
(널 문자도 비교 대상에 포함되므로 주의한다)


문자열 변환
  int atoi(const char * str); // int로 변환
  double atof( .. ) // float으로 변환
  long atol // long으로 변환





다음 글: 8. 구조체와 typedef