c언어 개념 정리와 문제 풀이를 통해 알고리즘 수업을 준비하는 과정입니다. 그동안 수강했던 c언어 수업 자료와 윤성우의 열혈 C 프로그래밍의 내용을 포함해 저에게 기록할 가치가 있는 내용이 담겨 있습니다.
파일과 스트림
파일은 운영체제에 의해서 관리가 되기 때문에 운영체제와 파일의 관계를 먼저 이해해야 한다.
프로그램상에서 파일에 저장되어 있는 데이터를 참조하고 싶을 때 가장 먼저 해야 할 일은
프로그램과 파일 사이에 데이터가 이동할 수 있는 다리를 놓는 일이다.
데이터 이동의 경로가 되는 다리를 '스트림'이라 하고, 앞선 포스팅에서 다룬 적 있다.
즉 프로그램과 파일 사이에 스트림을 형성해야 데이터를 주고 받을 수 있다.
스트림이라는 것은 운영체제에 의해서 형성되는 소프트웨어적인 상태를 의미하는 것일 뿐,
실제로 물리적 다리가 놓여지는 것은 아니다.
프로그램과 파일 사이에 스트림이 형성되었다 = "파일로부터 데이터를 읽어 들일/파일에 데이터를 쓸 기본 준비 끝"
파일은 운영체제에 의해서 그 구조가 결정되고 관리되기 때문에 파일 뿐 아니라 스트림 형성도 운영체제의 몫이다.
fopen 함수 호출을 통한 파일과의 스트림 형성과 FILE 구조체
#include <stdio.h>
FILE * fopen(const * filename, const char * mode);
위의 함수는 스트림을 형성할 때 호출하는 함수이다.
fopen 함수 호출을 통해서 프로그램상에서 파일과의 스트림을 형성할 수 있다.
함수의 첫 번째 인자에는 스트림을 형성할 파일의 이름, 두 번째 인자에는 형성할 스트림의 종류에 대한 정보를 문자열의 형태로 전달한다. 이 함수는 해당 파일과 스트림을 형성하고 스트림 정보를 FILE 구조체 변수에 담아서 그 변수의 주소 값을 반환한다.
FILE 구조체 변수의 멤버에 직접 접근할 일이 없으므로 FILE 구조체가 어떻게 정의되어 있는지 알 필요가 없다.
위 함수가 반환하는 FILE 구조체의 포인터는 파일을 가리키기 위한 용도로 사용된다. 이 포인터를 이용해서 파일에 데이터를 저장하거나 파일에 저장된 데이터를 읽게 된다.
프로그램상에서 fopen 함수를 호출할 때 다음 3가지를 주목하면 된다.
1. fopen 함수가 호출되면 FILE 구초제 변수가 생성된다.
2. 생성된 FILE 구조체 변수에는 파일에 대한 정보가 담긴다.
3. FILE 구조체의 포인터는 사실상 파일을 가리키는 '지시자' 역할이다.
입력 스트림과 출력 스트림의 생성
스트림은 '한 방향으로 흐르는 데이터의 흐름'이므로, 데이터를 파일로부터 읽어 들이기 위한 '입력 스트림'과 데이터를 파일에 쓰기 위한 '출력 스트림'으로 구분된다.
fopen 함수의 두 번째 인자에는 "wt", "rt"가 들어갈 수 있다.
'wt 모드의 스트림'은 텍스트 데이터를 쓰기 위한 출력 스트림이고,
'rt 모드의 스트림'은 텍스트 데이터를 읽기 위한 입력 스트림이다.
fclose 함수
#include <stdio.h>
int fclose(FILE * stream);
스트림의 소멸을 요청하는 함수이다. fopen 함수의 반대 기능을 제공한다.
fopen 함수는 스트림을 형성하는 함수이고, fclose 함수는 스트림을 해제하는 함수이다.
fopen 함수는 파일을 개방하는 함수이고, fclose 함수는 파일을 닫는 함수이다.
fclose 함수 호출을 통해서 개방되었던 파일을 닫아줘야 하는 2가지 이유가 있다.
1. 운영체제가 할당한 자원의 반환
함수 호출을 통해서 스트림의 형성을 요청하는 것은 우리지만, 실제로 스트림을 형성하는 주체는 운영체제이다.
운영체제는 스트림 형성을 위해서 시스템의 자원(주로 메모리)을 할당한다.
근데 이 자원은 파일을 닫아주지 않으면 할당된 채로 남아있게 되어, 그만큼의 자원손실을 초래하기 때문에 파일의 사용이 끝나는 즉시 자원을 반환해줄 필요가 있다.
2. 버퍼링 되었던 데이터의 출력
앞서 콘솔 스트림의 중간에 존재하는 입력버퍼, 출력버퍼를 얘기한 적 있다.
마찬가지로 파일 스트림의 경우에도 중간에 입력버퍼, 출력버퍼가 존재한다.
운영체제는 프로그램과 파일 사이에 입출력 버퍼를 둬서 성능 향상을 도모하는 것이다.
fputc와 같은 함수의 호출로 데이터를 파일로 전송한다고 해서 파일에 바로 저장되는 것이 아니라, 일단은 출력버퍼에 저장되었다가 운영체제가 정해놓은 버퍼링 방식에 따라서 뒤늦게 파일에 저장이 된다.
근데 문자가 출력버퍼에 존재하는 상태에서(파일에 저장되기 직전의 상태에서) 컴퓨터 전원이 꺼진다면?
문자는 파일에 저장되지 않는다.
그러나 fclose 함수 호출을 통해서 파일을 닫아주면 출력버퍼에 저장되어 있던 데이터가 파일로 이동하면서 출력버퍼는 비워지게 된다. 때문에 사용이 끝난 파일은 곧바로 fclose 함수를 호출해 주는 것이 좋다.
fflush 함수
#include <stdio.h>
int fflush(FILE * stream);
스트림을 종료하지 않고 버퍼만 비우고 싶을 때 사용하면 된다.
출력버퍼를 비운다는 것은 출력버퍼에 저장된 데이터를 목적지로 전송한다는 의미고,
입력버퍼를 비운다는 것은 입력버퍼에 저장된 데이터를 소멸시킨다는 의미이다.
fflush 함수는 출력버퍼를 비우는 함수이다.
fflush 함수는 입력버퍼를 대상으로 호출할 수 없다.
fflush(fp); 는 출력버퍼를 비우라는 요청이다.
그러면 출력버퍼에 저장된 데이터가 실제로 파일에 저장된다.
파일에 데이터 쓰기
#include <stdio.h>
int main(void)
{
FILE * fp = fopen("data.txt", "wt");
if (fp == NULL) {
puts("파일 오픈 실패!");
return -1;
}
fputc('A', fp);
fputc('B', fp);
fclose(fp);
return 0;
}
파일에 AB가 저장된다.
파일 데이터 읽기
#include <stdio.h>
int main(void)
{
int ch, i;
FILE * fp = fopen("data.txt", "rt");
if (fp == NULL) {
puts("파일 오픈 실패!");
return -1;
}
for (i=0; i<3; i++) {
ch = fgetc(fp);
printf("%c \n", ch);
}
fclose(fp);
return 0;
}
fgetc 함수는 파일에 저장된 문자 하나를 반환하는 함수이다.
FILE 구조체 포인터 fp가 지칭하는 파일에 저장된 문자 하나가 반환되어 변수 ch에 저장된다.
'Algorithm > C' 카테고리의 다른 글
[c언어 개념] #15 메모리 구조, 메모리의 동적 할당 (malloc, free, calloc, realloc) (0) | 2022.08.02 |
---|---|
[c언어 개념] #14 파일의 개방 모드, 파일 입출력 함수, 파일 위치 지시자 (0) | 2022.07.27 |
[c언어 개념] #12 구조체, 구조체 배열, typedef, 공용체, 열거형 (0) | 2022.07.25 |
[c언어 개념] #11 문자와 문자열 함수 (0) | 2022.07.25 |
[c언어 개념] #10 함수 포인터, void 포인터 (0) | 2022.07.24 |