[C언어] 7. 파일 입출력




제가 책(윤성우의 열혈 C 프로그래밍)을 보다가 까먹었던 부분만 포스팅 하는 것입니다!

순서가 뒤죽박죽이어도 이해해주세요~~!

파일과 스트림



파일로부터 데이터를 읽어 들이려면(파일에 데이터를 쓰려면)

프로그램과 파일 사이에 스트림이 형성되어야 합니다.

파일은 운영체제에 의해서 그 구조가 결정되고 관리되는 대상이기 때문에,

파일 뿐만 아니라 스트림의 형성도 운영체제의 몫입니다.

스트림을 형성할 때 호출하는 함수는 fopen입니다~

#include <stdio.h>
FILE * fopen(const char *filename,const char * mode);

//성공 시 해당 파일의 FILE 구조체 변수의 주소 값, 실패 시 NULL 포인터 반환


//FILE * 는 파일을 가리키는 포인터라고 생각!



예를 들어 다음과 같이 사용합니다.

#include <stdio.h>

int main(void)
{
	FILE *fp=fopen("data.txt","wt"); //텍스트 데이터 출력용 스트림 형성
	
	if(fp==NULL){
		printf("파일오픈실패");
		return -1;
	}
	
	fputc('A',fp); //fp가 지칭하는 data.txt에 문자 A가 저장됩니다.
	
	fclose(fp); //스트림의 종료
	return 0;
}



파일로부터 데이터를 읽어드리는 건 다음과 같습니다.

#include <stdio.h>

int main(void)
{
	FILE *fp=fopen("data.txt","rt"); //텍스트 데이터 입력용 스트림 형성
	
	if(fp==NULL){
		printf("파일오픈실패");
		return -1;
	}
	
	int ch=fgetc(fp); 
	//fp가 지칭하는 data.txt에 있는 문자 A를 가져와 ch에 저장합니다.
	
	fclose(fp); //스트림의 종료
	return 0;
}



그리고 개방되었던 파일은 fclose 함수로 닫아주어야 합니다~



파일의 개방모드



fopen 함수의 두 번째 인자로 “wt”와 “rt” 를 전달하여 스트림을 형성하였습니다.

형성할 수 있는 스트림의 종류는 훨씬 더 다양합니다.

rt : 텍스트모드 읽기가능

wt : 텍스트모드 쓰기가능

at : 텍스트모드 파일의 끝에 덧붙여 쓰기 가능

rb : 바이너리모드 읽기가능

wb : 바이너리모드 쓰기가능

ab : 바이너리모드 파일의 끝에 덧붙여 쓰기 가능



바이너리 파일은 영상, 음원파일 등 컴퓨터가 인식하는 데이터를 담고 있는 파일이고,

텍스트 파일은 사람이 인식할 수 있는 문자를 담고 있는 파일입니다.

모드를 텍스트, 바이너리로 나누는 이유는 대표적으로 \n 입니다.

개행은 일반적으로 c언어에서만 ‘\n’ 으로 사용합니다.

MS-DOS의 파일 내 개행은 ‘\r\n’ 입니다

그렇기에 파일을 텍스트 모드로 개방하면 자동으로 ‘\r\n’을 ‘\n’으로 바꾸어 줍니다~!



파일 입출력 함수의 기본



개방된 파일(텍스트 파일) 대상으로의 데이터 입력과 출력 함수는 다음과 같습니다.

int fputc(int c, FILE * stream)

int fgetc(FILE * stream)

int fputs(const char* s, FILE * stream)

char * fgets(char* s, int n, FILE * stream)



구체적인 방법은 5.문자와 문자열 관련 함수 를 참고 하시길 바랍니다.



feof 함수 기반의 파일복사 프로그램



파일의 끝을 확인하는 함수는 다음과 같습니다.

#include <stdio.h>

int feof(FILE * stream);

//파일의 끝에 도달한 경우 0이 아닌 값 반환



이를 이용하여 파일 복사 프로그램을 작성해보겠습니다~

#include <stdio.h>

int main(void)
{
	FILE * src=fopen("src.txt","rt");
	FILE * des=fopen("des,txt","wt");
	int ch;
	
	if(src==NULL||des==NULL)
	{
		printf("파일오픈실패");
		return -1;
	}
	
	while((ch=fgetc(src))!=EOF)
		fputc(ch,des);

	if(feof(src)!=0) //파일 src 끝까지 복사됐는지 확인
		printf("파일복사 완료!");
	else
		printf("파일복사 실패!");
	
	fclose(src);
	fclose(des);
	
	return 0;
}



바이너리 데이터의 입출력: fread, fwrite



바이너리 데이터의 입력에 사용되는 함수는 다음과 같습니다.

int buf[12];

fread((void*)buf,sizeof(int),12,fp); //fp는 FILE 구조체 포인터



위의 호출문은 다음의 의미로 해석됩니다.

sizeof(int) 크기의 데이터 12개를 fp로부터 읽어 들여서 배열 buf에 저장하라!

그리고 바이너리 데이터의 출력에 사용되는 함수는 다음과 같습니다.


int buf[7]={1,2,3,4,5,6,7};

fwrite((void*)buf,sizeof(int),7,fp);



위의 호출문은 다음의 의미로 해석됩니다.

sizeof(int) 크기의 데이터 7개를 buf로부터 읽어서 fp에 저장하라!



서식에 따른 데이터 입출력: fprintf, fscanf



두 개의 텍스트 데이터와 하나의 바이너리 데이터를 출력해야 하는 상황에서,

제일 먼저 생각할 수 있는 방법은 fscanf 함수와 fprintf 함수의 호출입니다.

fprintf 호출방법은 다음과 같습니다.

char name[10]="꾸리"; //텍스트 데이터

char sex='M'; //텍스트 데이터 남자 M

int age=24; //바이너리 데이터

fprintf(fp, "%s %c %d",name,sex,age); //fp는 FILE 구조체 포인터

//텍스트 데이터와 바이너리 데이터가 하나의 문자열로 묶여져서 파일에 저장됩니다.



fscanf 호출방법은 다음과 같습니다


char name[10];

char sex; 

int age; 

fscanf(fp, "%s %c %d",name,&sex,&age); //fp는 FILE 구조체 포인터

//단, 데이터 읽는 순서와 데이터의 저장순서가 일치해야 합니다.



그리고 구조체 변수를 파일로부터 입출력 할 때도,

바이너리 입출력 함수 fwrite와 fread를 이용합니다.



파일 위치 지시자



경우에 따라서는 파일의 중간 또는 마지막 부분에

저장된 데이터의 일부를 읽어야하는 경우도 있습니다~

이 때는 ‘파일 위치 지시자’라는 것을 파일의 중간 또는 마지막 부분에

이동시켜야 합니다.

파일 위치 지시자란? 

FILE 구조체의 멤버 중에는 파일의 위치 정보를 저장하고 있는데,

이 멤버의 값은 fgets,fputs와 같은 함수가 호출될 때마다 갱신됩니다.

예를 들어 fgets 함수 호출의 경우 

이 지시자가 가리키는 위치를 시작으로 문자열을 읽어 들입니다.



파일 위치 지시자의 이동은 fseek함수를 사용하며 다음과 같습니다.


fseek(fp,2,SEEK_SET); //파일 맨 처음에서 오른쪽으로 2번째로 이동

fseek(fp,2,SEEK_CUR); //현재 위치에서부터 오른쪽으로 2번째로 이동

fseek(fp,-2,SEEK_END); //파일 맨 끝에서 왼쪽으로 2번째로 이동


SEEK_SET(0), SEEK_CUR(1), SEEK_END(2)

//SEEK_END에서 말하는 파일의 끝은 EOF를 의미합니다



그리고 파일 위치 지시자의 현재 위치를 반환할 때는 ftell함수를 사용합니다.


long fpos=ftell(fp);

//ftell의 반환형은 long입니다.