티스토리 뷰

주의 사항!

  • 이 일지는 작성하고 있는 현시점, 공부와 병행하면서 작성되고 있습니다.
  • 공부 중에 떠오른 생각이나 그때그때의 개념정리 같은 내용이 포함됩니다.
  • 따라서 이 일지의 내용은 제가 공부하고 이해한 대로 작성되기 때문에 실제 사실과는 다를 수 있습니다.

파일 입출력은 파일에 저장된 데이터 형태에 따라 프로그래밍 방식이 달라집니다.

따라서 데이터 형태에 맞는 입출력 함수를 사용하는 것이 좋습니다.

 

파일에서 데이터를 한 줄씩 입력할 때는 fgets함수를 사용합니다.

반면 문자열을파일에 출력할 때는 fputs 함수를 사용합니다.

다음 예제를 통해 사용법을 살펴보겠습니다.

#include <stdio.h>
#include <string.h>

int main(void)
{
	FILE* pInFile, * pOutFile;
	
	pInFile = fopen("a.txt", "r");
	if (pInFile == NULL)
	{
		printf("파일을 읽지 못했습니다.\n");
		return 1;
	}

	pOutFile = fopen("b.txt", "w");
	if (pOutFile == NULL)
	{
		printf("파일을 새로 생성하지 못했습니다.\n");
		return 1;
	}

	while (1)
	{
		char str[80];
		char* res;

		res = fgets(str, sizeof(str), pInFile);
		if (res == NULL)
		{
			break;
		}

		str[strlen(str) - 1] = '\0';
		fputs(str, pOutFile);
		fputs(" ", pOutFile);
	}

	fclose(pInFile);
	fclose(pOutFile);
	return 0;
}

위 예제에서 a.txt 파일의 내용을 b.txt파일에 옮겨 적고 있습니다.

pOutFile = fopen("b.txt", "w");

출력할 파일을 쓰기 전용 텍스트 파일로 개방했습니다.

만약 b.txt라는 이름의 파일이 없다면 해당 이름의 파일을 생성합니다.

res = fgets(str, sizeof(str), pInFile);

fgets 함수는 pInFile 스트림 파일의 버퍼에서 데이터를 읽어서 str에 저장합니다.

단, 최대 sizeof(str) 만큼의 데이터만 읽고 저장합니다.

그리고 만약 버퍼에 읽어들일 데이터가 남아있지 않다면 fgets 함수는 NULL포인터를 반환합니다.

str[strlen(str) - 1] = '\0';
fputs(str, pOutFile);
fputs(" ", pOutFile);

fgets 함수는 마지막 개행문자까지도 입력하기 때문에 a.txt에 저장된 문자열 끝에 엔터로 줄바꿈이 되어 있다면

str에도 이 개행문자가 저장됩니다. 따라서 이 개행문자를 널문자로 바꿔줍니다.

fputs 함수는 pOutFile 스트림 파일의 버퍼에 str이 가지고 있는 데이터를 출력합니다.

그리고 스페이스 바도 한 번 출력해줍니다.

 

이후 실행결과는 다음과 같습니다.

a.txt b.txt
apple
banana
strawberry

apple banana strawberry




파일에 저장된 문자열을 숫자로 변환하여 입력할 때는 fscanf 함수를 사용합니다.

반대로 정수나 실수를 쉽게 파일에 출력하는 때는 fprintf 함수를 사용합니다.

이들은 scanf, printf 함수와 같은 기능을 수행하지만 파일을 지정할 수 있습니다.

예제를 통해 사용법을 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
	FILE* pInFile, * pOutFile;

	pInFile = fopen("a.txt", "r");
	if (pInFile == NULL)
	{
		printf("파일을 열지 못했습니다.\n");
		return 1;
	}
	
	pOutFile = fopen("b.txt", "w");
	if (pOutFile == NULL)
	{
		printf("파일을 새로 생성하지 못했습니다.\n");
		return 1;
	}

	while (1)
	{
		char name[20];
		int kor, eng, mat, tot, res;
		double avg;

		res = fscanf(pInFile, "%s%d%d%d", name, &kor, &eng, &mat);
		if (res == EOF) break;

		tot = kor + eng + mat;
		avg = tot / 3.0;
		fprintf(pOutFile, "%s%5d%7.1lf\n", name, tot, avg);
	}

	fclose(pInFile);
	fclose(pOutFile);
	return 0;
}

a.txt파일에는 각 학생의 이름과 국어, 영어, 수학 점수가 기록되어 있습니다.

그리고 해당 예제는 a.txt파일의 데이터를 읽어 학생의 이름과 총점, 평균점수를 계산하고 b.txt파일에 저장합니다.

res = fscanf(pInFile, "%s%d%d%d", name, &kor, &eng, &mat);

fscanf 함수는 scanf함수와 형태가 매우 비슷합니다.

다만 첫번째 인수에 사용할 스트림 파일을 넣어줍니다.

위 함수는 pInFile 스트림 파일의 버퍼를 통해 이름, 국어, 영어, 수학 점수를 입력받습니다.

만약 해당 파일에 남은 데이터가 없다면 fscanf 함수는 EOF를 반환합니다.

 

a.txt의 점수들은 숫자형태이지만 텍스트 파일이기 때문에 모두 문자입니다.

fscanf 함수가 이들을 읽으면 데이터를 입력할 변수의 형태에 맞게 자동으로 변환합니다.

fprintf(pOutFile, "%s%5d%7.1lf\n", name, tot, avg);

fprintf 함수도 printf 함수와 매우 유사합니다.

마찬가지로 첫 번째 인수로 출력할 스트림 파일을 넣어줍니다.

위의 함수는 pOutFile 스트림 파일의 버퍼를 통해 이름과 총점, 평균점수를 파일로 출력합니다.

 

fprintf 함수는 fscanf 함수와는 반대로 파일에 출력할 모든 데이터를 문자의 형태로 변환합니다.

그리고 출력 과정에서 에러가 발생하면 음수를 반환합니다.

 

실행결과는 다음과 같습니다.

a.txt b.txt
꽁색시 95 99 96
또서방 80 85 94
뽐뽀미 92 76 93
꽁색시 290 96.7
또서방 259 86.3
뽐뽀미 261 87.0

스트림 파일을 사용하는 입출력 함수들이 버퍼를 공유하면 예상과 다른 결과가 나올 수 있습니다.

예제를 통해 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
	FILE* pFile;
	
	pFile = fopen("a.txt", "r");

	int age;
	char name[20];

	fscanf(pFile, "%d", &age);
	fgets(name, 20, pFile);

	printf("나이 : %d, 이름 : %s\n", age, name);
	fclose(pFile);

	return 0;
}

/*
실행결과

나이 : 17, 이름 :


*/
a.txt
17
KOEY

fscanf 함수와 fgets 함수는 개행문자를 처리하는 방식이 다릅니다.

우선 fscanf 함수는 개행문자, 스페이스바, 탭 문자와 같은 화이트 스페이스를 데이터로 입력하지 않습니다.

따라서 처음 나이를 입력할 때 17까지만 입력하고 그 뒤의 개행문자는 입력하지 않습니다.

그래서 pFile 스트림 파일의 버퍼에는 개행문자가 남게 됩니다.

 

그런데 fgets함수는 개행문자가 나올 때까지 데이터를 입력하며 그 개행문자까지 입력에 포함합니다.

fscanf 함수 다음에 fgets 함수가 호출되면 버퍼에는 KOEY 이전에 개행문자가 남아 있으므로

이 개행문자를 입력하고 입력을 종료합니다.

따라서 name에는 개행문자 하나만 저장됩니다.

 

이런문제를 방지하기 위해서는 fscanf 함수가 호출된 다음, 버퍼에 남아 있는 개행문자를

fgetc 함수를 이용해 버려주는 방법도 있습니다.

 


fflush 함수에 파일 포인터를 인수로 주어 해당 스트림 파일의 버퍼의 내용을 모두 지울 수도 있습니다.

반환값은 0이며 버퍼를 지우지 못했을 때는 EOF를 반환합니다.

 

입력 스트림 파일에 fflush 함수를 사용하면 입력 버퍼의 내용을 모두 지웁니다.

하지만 이 함수는 입력 파일에 대해서는 표준이 정의되어 있지 않습니다.

따라서 시스템에 따라 다르게 동작할 수 있습니다.

입력 파일에 대해서는 fflush 함수를 사용하지 않는 것이 좋습니다.

 

단, 출력 파일에 사용하면 다르게 작동합니다.

출력 파일의 포인터를 주면 버퍼에 남은 데이터를 연결된 장치로 바로 출력합니다.

 


fread 함수와 fwrite 함수는 입출력할 데이터의 크기와 개수를 인수로 줄 수 있습니다.

그래서 구조체나 배열과 같이 데이터 양이 많은 경우도 파일에 쉽게 입출력할 수 있습니다.

 

그리고 이들 함수는 숫자 데이터를 문자로 변환하지 않습니다.

무슨 말인지 예제를 통해 살펴보겠습니다.

#include <stdio.h>

int main(void)
{
	FILE* pFileA, * pFileB;

	pFileA = fopen("a.txt", "wt");
	if (pFileA == NULL)
	{
		printf("파일을 생성하지 못했습니다.\n");
		return 1;
	}

	pFileB = fopen("b.txt", "wb");
	if (pFileB == NULL)
	{
		printf("파일을 생성하지 못했습니다.\n");
		return 1;
	}

	int num = 10;
	int res;

	fprintf(pFileA, "%d", num);
	fwrite(&num, sizeof(num), 1, pFileB);

	fclose(pFileA);
	fclose(pFileB);

	pFileB = fopen("b.txt", "rb");
	fread(&res, sizeof(res), 1, pFileB);
	printf("%d", res);

	return 0;
}

/*
실행결과

10
*/
pFileA = fopen("a.txt", "wt");
if (pFileA == NULL)
{
	printf("파일을 생성하지 못했습니다.\n");
	return 1;
}
pFileB = fopen("b.txt", "wb");
if (pFileB == NULL)
{
	printf("파일을 생성하지 못했습니다.\n");
	return 1;
}

pFileA는 텍스트 파일로 개방하였고, pFileB는 바이너리 파일로 개방했습니다.

fprintf(pFileA, "%d", num);
fwrite(&num, sizeof(num), 1, pFileB);

pFileA에 fprintf 함수를 이용해 num의 데이터를 출력했습니다.

반면 pFileB의 경우는 fwrite 함수를 이용해 num의 데이터를 출력했습니다.

 

fwrite 함수의 첫번째 인자는 출력할 데이터의 시작주소를 알려줍니다.

두 번째 인자는 데이터의 크기를 알려줍니다.

세 번째 인자에는 출력할 데이터의 개수를 알려줍니다.

따라서 이 함수가 출력하는 데이터는 시작주소인 &num부터 시작해서 총 sizeof(num) * 1바이트가 됩니다.

마지막 네 번째 인수에는 사용할 스트림 파일을 알려줍니다.

 

fwrite 함수는 출력한 데이터 수를 반환합니다.

 

이후 함수를 닫아주고 pFileB를 바이너리 파일 읽기 개방 모드로 개방했습니다.

fread(&res, sizeof(res), 1, pFileB);

그리고 fread 함수를 이용해 pFileB에 있는 데이터를 입력받습니다.

함수의 첫 번째 인수에는 해당 데이터를 저장할 변수의 시작주소를 알려줍니다.

두 번째 인수에는 해당 변수의 크기를 알려줍니다.

세 번째 인수에는 데이터의 개수를 알려줍니다.

그리고 마지막 네 번째 인수에는 사용할 스트림 파일을 알려줍니다.

 

fread 함수는 읽은 데이터의 수를 반환합니다.

 

프로그램을 실행하고 직접 a.txt파일과 b.txt파일을 열어서 내용을 확인해봅니다.

그러면 a.txt파일에는 10이라고 적혀있지만 b.txt파일에는 아무것도 적혀있지 않습니다.

하지만 이건 아무것도 적혀있지 않은 것이 아니라 10이라는 데이터를 저장하고 있지만 보이지 않을 뿐입니다.

 

fwrite, fread 함수는 숫자를 문자로 변환하지 않습니다.

그런데 텍스트 파일은 데이터를 저장할 때 숫자도 아크키코드값에 따라 문자로 변환해야 합니다.

따라서 fprintf 함수로 a.txt 파일에 10을 저장하면 각각 1바이트의 문자인 '1', '0' 으로 변환하여 저장합니다.

하지만 fwrite 함수로 b.txt 파일에 10을 저장하면 문자로 변환하지 않고 4바이트의 숫자로서 저장을 하게 됩니다.

그런데 그렇게 저장한 파일이 텍스트 파일이기 때문에 안의 데이터가 정상적으로 보이진 않게 됩니다.

 

b.txt파일을 개방할 때 바이너리 파일로 개방한 것도 이와 같은 이유에서 입니다.

숫자를 문자로 변환하지 않는 함수를 사용할 것이기 때문에,

모두 문자의 형태로 저장해야 하는 텍스트 파일에는 맞지 않습니다.

 

다음 시간엔 파일 입출력과 관련하여 실전문제를 풀어보겠습니다.

'공부 일지 > C언어 공부 일지' 카테고리의 다른 글

파일 입출력 실전문제2  (2) 2021.02.01
파일 입출력 실전문제1  (0) 2021.02.01
파일 입출력  (0) 2021.02.01
사용자 정의 자료형 실전문제2  (0) 2021.01.31
사용자 정의 자료형 실전 문제1  (0) 2021.01.31
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/01   »
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 30 31
글 보관함