본문 바로가기
코스웨어/16년 스마트컨트롤러

2016-08-25_조재찬_스터디일지_C언어-파일 입출력

by 알 수 없는 사용자 2016. 8. 25.
728x90
반응형
입력 스트림과 출력 스트림의 형성을 요청하는 fopen 함수

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

fopen함수를 통해 FILE 구조체 변수가 생성, 파일과의 스트림 형성
FILE 구조체 변수에 파일 정보가 담김 (파일 데이터가 담기는 것이 아님)

FILE * fp = fopen("data.txt", "wt");  // wt(write in text mode)모드로 파일 data.txt와 출력스트림을 형성
FILE * fp = fopen("C:\\Project\\data.txt", "wt");    // 경로 지정시 \ 자체를 표현하려면 \\ 두개 써야함

FILE * fp = fopen("data.txt", "rt");    // rt모드는 입력스트림을 형성

fputc('A', fp); // fp가 지칭하는 파일 data.txt에 문자 A 저장


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main()
{
	FILE * fp = fopen("data.txt", "wt");	
	if (fp == NULL)
	{
		puts("File open failed");
		return -1;
	}

	fputc('A', fp);	//	출력의 대상을 파일로 지정.
	fputs("BC", fp);
	fclose(fp);

	return 0;
}





스트림의 소멸을 요청하는 fclose 함수


#include <stdio.h>

int fclose(FILE * stream);



fclose 함수가 가지는 의미 (써야하는 이유)


  • OS는 스트림 형성을 위해 시스템의 자원(주로 메모리)을 할당한다. 이 때 fclose함수를 통해 자원을 반환해준다.
  • 두번째로 fclose 함수를 통해 버퍼에 있던 데이터가 출력이 되게 된다. 출력버퍼에 데이터가 존재하는 상황에서 비정상 종료가 될 때 소멸될 수 있는 문제도 있을 수 있다. 이 때문에 버퍼의 데이터가 파일로 바로 저장되도록 fclose 함수를 호출해주는 것이 좋다.

출력 버퍼를 비우는 fflush 함수

앞에서 stdout을 대상으로 호출했듯이 출력버퍼를 대상으로 호출한다. (wt모드)

입력버퍼를 비우려면 데이터를 읽으면 된다. 따라서 입력버퍼를 비우는 함수는 따로 존재하지 않는다.




파일 입출력 예제 (파일을 생성해서 문자와 문자열 저장후 읽어들이기)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int main(void)
{
	FILE * fp=fopen("simple.txt", "wt");
	if(fp==NULL) {
		puts("파일오픈 실패!");
		return -1;
	}

	fputc('A', fp);
	fputc('B', fp);
	fputs("My name is Hong \n", fp);
	fputs("Your name is Yoon \n", fp);
	fclose(fp);
	return 0;
}


바로 위 예제 13행에서 \n을 넣지않으면 읽기를 했을 때 문자가 생길 수 있다. 

문자열이 파일에 저장할 때는 null문자가 저장되지 않고, 때문에 파일에서는 개행을 기준으로 문자열을 구분하기 때문이다.


#include <stdio.h>

int main(void)
{
	char str[30];
	int ch;
	FILE * fp=fopen("simple.txt", "rt");
	if(fp==NULL) {
		puts("파일오픈 실패!");
		return -1;
	}

	ch=fgetc(fp);
	printf("%c \n", ch);
	ch=fgetc(fp);
	printf("%c \n", ch);

	fgets(str, sizeof(str), fp);
	printf("%s", str);
	fgets(str, sizeof(str), fp);
	printf("%s", str);

	fclose(fp);
	return 0;
}


읽어 들일 때는 write순서대로 read해야 저장된 값대로 읽어지는 것을 볼 수 있다. (저장된 값의 순서대로 fputc는 fgetc로 fputs는 fgets로 읽는다.)




스트림은 크게 텍스트 모드 스트림과 바이너리 모드 스트림으로 나뉜다.

텍스트 모드는 사람이 인식할 수 있는 문자 데이터를 저장하고,

바이너리 모드는 컴퓨터가 인식할 수 있는 유형의 데이터이다.


물론 메모리상에는 2진의 데이터로 존재하지만,

문자 데이터는 쉽게 말해 windows 메모장으로 열어서 사람이 쉽게 인식이 가능한 것을 말한다.


그 외에 음원,영상,그림 등은 바이너리 데이터에 포함된다.


스트림 모드중 a mode는 파일의 끝에 덧붙여 쓰는 것으로

파일이 없을 때 생성하는 점은 같지만, 매번 새로 데이터를 쓰는 점은 w와는 다르다.


이외에 + 모드는 읽기/쓰기가 모두 가능한 스트림이지만

양방향 스트림이 실제적으로 쓰이는 경우는 거의 없다고 보면 된다. (필요할 때 스트림을 열고 닫는것이 보통)


텍스트 스트림이 별도로 존재하는 이유

c언어 에서는 \n이 개행을 의미한다.


하지만 os별로 개행의 표시방법이 다르기 때문에

os에 따라 자동변환이 이루어질 수 있는 텍스트 모드를 사용한다.

(읽을 때는 다시 \n으로 복구)


Unix/Linux : \n

Mac : \r

Windows : \r\n



feof 함수


#include <stdio.h>

int feof (FILE * stream);

파일의 끝(End Of File)에 도달한 경우 0이 아닌 값 반환 (읽어들일 데이터가 더 이상 없음을 의미)


파일 입력함수는 오류가 발생했을 때에도 EOF를 반환한다. 아래의 문자와 문자열 단위 파일복사 프로그램에서

파일에 끝에 도달한건지, 오류가 발생한건지 체크를 할 수 있다.


#include <stdio.h>

int main(void)
{
	FILE * src = fopen("src.txt", "rt");	// 미리 만들어진 src.txt 파일을 text mode로 읽음
	FILE * des = fopen("dst.txt", "wt");	// src.txt의 text를 dst.txt파일에 text mode로 씀
	int ch;

	if (src == NULL || des == NULL)
	{
		puts("파일 열기 실패!");
		return -1;	// -1은 오류를 의미
	}

	while ((ch=fgetc(src)) != EOF)
	{
		fputc(ch, des);
	}

	if (feof(src) != 0)
		puts("파일 복사완료!");	// 0이 아닌 값을 반환하면 EOF에 도달했음을 의미
	else
		puts("파일 복사실패..");

	fclose(src);
	fclose(des);

	return 0;
}

정상적으로 파일이 복사되면 파일 복사완료! 메시지와 함께 src.txt파일의 text가 dst.txt파일에 복사된 것을 볼 수 있다.




바이너리 데이터의 입력/출력을 위한 함수 fread/fwrite


fread함수

#include <stdio.h>
size_t fread(void * buffer, size_t size, size_t count, FILE *stream)

int main(void)
{
(FILE * fp)=fopen(...
int buf[12]; // buffer
...
fread ((void *)buf, sizeof(int), 12, fp);
// sizeof(int)의 데이터 12개를 fp로 부터 읽어들이고 배열 buf에 저장

size는 최대가 아니라 무조건 그 크기만큼 읽어들이라는 의미이다.
성공시 전달인자 count만큼 반환하게 된다.
EOF 도달시 또는 실패시 count보다 작은 값을 반환한다.


fwrite 함수

#include <stdio.h>

size_t fwrite(void * buffer, size_t size, size_t count, FILE *stream)


fread와 마찬가지로 size는 그 크기만큼 쓰라는 의미이며,

성공시 전달인자 count만큼 반환하게 된다.
실패시 count보다 작은 값을 반환한다.



/ * fread/fwrite 를 이용한 바이너라 파일 복사 프로그램 예제 

물론 텍스트 모드도 복사의 대상이 될 수 있다.

*/

#include <stdio.h>

int main(void)
{
	FILE * src = fopen("src.bin", "rb");
	FILE * des = fopen("dst.bin", "wb");
	char buf[20];
	int readCnt;

	if (src == NULL || des == NULL) {
		puts("파일오픈 실패!");
		return -1;
	}

	while (1)
	{
		readCnt = fread((void*)buf, 1, sizeof(buf), src);
		// src에서 1 * 20(buf)만큼 읽어 buf에 저장

		if (readCnt < sizeof(buf))
			// 읽어들인 데이터 크기가 buf[20]의 size보다 작을 때
		{
			if (feof(src) != 0)
				// 에러는 아니지만 20바이트를 못 채웠고 EOF를 반환했을 때
			{
				fwrite((void*)buf, 1, readCnt, des);
				// 마지막으로 읽은 데이터를 파일에 저장
				puts("파일복사 완료");
				break;
			}
			else
				puts("파일복사 실패");

			break;
		}
		fwrite((void*)buf, 1, sizeof(buf), des);

	}

	fclose(src);
	fclose(des);
	return 0;
}



텍스트 데이터와 바이너리 데이터를 동시 입/출력하기 위한 함수 fprintf / fscanf


fprintf와 fscanf의 사용법은 간단하다. 다만 FILE을 대상으로 조합된 형태대로 입출력하는 점이 다르다.


// frpintf 예제


#include <stdio.h>

int main(void)
{
	char name[10];
	char sex;
	int age;

	FILE * fp=fopen("friend.txt", "wt"); // 저장 데이터가 문자열이므로 text mode로 open
	int i;

	for(i=0; i<3; i++)
	{
		printf("이름 성별 나이 순 입력: ");
		scanf("%s %c %d", name, &sex, &age);
		getchar(); // 입력시 \n가 들어가기 때문에 버퍼를 소멸시킴
		fprintf(fp, "%s %c %d", name, sex, age);
                // fp를 대상으로 조합이 된 문자열(차례대로 문자열,문자,정수)을 저장
	}
	fclose(fp);
	return 0;
}


/* fscanf 예제 

ret(return)  */

#include <stdio.h>

int main(void)
{
	char name[10];
	char sex;
	int age;

	FILE * fp = fopen("friend.txt", "rt");
	int ret;

	while (1)
	{
		ret = fscanf(fp, "%s %c %d", name, &sex, &age);
		if (ret == EOF)
			break;
		printf("%s %c %d \n", name, sex, age);
	}
	fclose(fp);
	return 0;
}



728x90