C - File input/output

[TOC]

스트림이란?

입력과 출력

스트림의 존재 이유

표준 입출력 함수

문자 단위 입출력 함수

문자 출력 함수

putchar()
int putchar(int c);
fputc()
int fputc(int c, FILE *stream);

문자 입력 함수

getchar()
int getchar(void);
fgetc()
int fgetc(FILE *stream);

EOF(WEOF)

문자 입력 함수의 반환형이 char가 아니라 int형인 이유

문자열 단위 입출력 함수

문자열 출력 함수

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

문자열 입력 함수

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

문자 입출력에서의 EOF

키보드 입력 스트림에서의 EOF

버퍼

메모리 버퍼

키보드의 입력이 입력 버퍼로 들어가는 시점

버퍼링을 하는 이유

출력 버퍼를 비우는 fflush 함수

#include <stdio.h>
int fflush(FILE *Stream);

입력버퍼를 비우기

while(getchar() != '\n');

파일 스트림

읽기/쓰기 모드

모드 스트림의 성격 파일이 없으면?
r(read) 읽기 가능 에러
w(write) 쓰기 가능 생성
a(append) 파일의 끝에 덧붙여 쓰기 가능 생성
r+ 읽기/쓰기 가능(파일이 존재할 경우 지우지 않는다) 에러
w+ 읽기/쓰기 가능(파일이 존재할 경우 모두 지운다) 생성
a+ 읽기/덧붙여 쓰기 가능 생성

텍스트 / 바이너리 파일

텍스트 파일의 개행문자

운영체제 별 개행문자

운영체제 개행문자
Windows \r\n
Mac \r
Unix \n

개행문자의 자동변환

바이너리 모드

파일 스트림 형성

#include <stdio.h>

/*
const char *filename : 스트림을 형성할 파일 이름
const char *mode : 형성하고자 하는 스트림의 종류
*/
FILE *fopen(const char *filename, const char *mode);

출력 스트림

fopen("filename", "wt" /*텍스트 쓰기 전용 출력 스트림*/);

입력 스트림

fopen("filename", "rt"/*텍스트 읽기 전용 입력 스트림*/);

파일 입출력 함수

출력 스트림에 데이터 쓰기

fputc()

fputc('A'/*int 값을 입력받으므로 문자열은 안된다*/, file);

todo 읽을 때마다 포인터 이동

파일의 끝인지 확인

feof()

#include <stdio.h>
int feof(FILE *stream);

바이너리 데이터 입출력

fwrite() - 바이너리 데이터 출력

#include <stdio.h>
/*
const void *buffer : 기록할 데이터 포인터
size_t size : 데이터 단위(데이터 형의 크기)
size_t count : 한 번에 기록할 데이터 개수
FILE *stream : 출력할 파일 스트림
return : 성공 시 count, 실패 시 count보다 작은 값
*/
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);
cout << "binary file output" << endl;
{
	FILE *pFile; // 파일 구조체 포인터 선언
	fopen_s(&pFile, "fopen_binary.bin", "wb"); // 파일 스트림 얻기

	for (size_t i = 0; i < NumCount; i++)
	{
		int buf = i;
		fwrite(&buf, sizeof(size_t), 1, pFile); // 파일에 int 데이터를 바이너리로 기록
	}

	fclose(pFile); // 파일 스트림 닫기
}

fread() - 바이너리 데이터 입력

#include <stdio.h>
/*
void *buffer : 데이터 저장할 버퍼
size_t size : 데이터 단위(데이터 형의 크기)
size_t count : 한번에 읽을 데이터 개수
FILE *stream : 입력할 파일 스트림
return : 성공 시 count, 실패 시 count보다 작은 값
*/
size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
cout << "binary file input" << endl;
{
	FILE *pFile; // 파일 구조체 포인터 선언
	fopen_s(&pFile, "fopen_binary.bin", "rb"); // 파일 입력 스트림 얻기

	int buf[NumCount] = {}; // 읽은 데이터를 저장할 버퍼
	fread(buf, sizeof(size_t), NumCount, pFile); // 파일의 바이너리를 size_t 크기로 읽기

	for (size_t i = 0; i < NumCount; i++)
	{
		cout << buf[i]; // 읽은 데이터를 하나씩 출력
	}
	cout << endl;

	fclose(pFile);
}

바이너리 파일 복사

cout << "mp3 file copy" << endl;
{
	FILE *pReadFile;
	FILE *pWriteFile;
	fopen_s(&pReadFile, "SampleAudio.mp3", "rb");
	fopen_s(&pWriteFile, "SampleAudio_Copy.mp3", "wb");
	
	size_t bufferSize = 1;
	size_t elementSize = sizeof(unsigned char);
	size_t elementCount = 1;
	for (size_t i = 0; i < 500000; i++) // 434KB(443,926 bytes) 파일을 복사
	{
		unsigned char buf;
		size_t result = fread_s(&buf, bufferSize, elementSize, elementCount, pReadFile);

		// 입력 파일 스트림의 끝인지 검사
		// result가 elementCount보다 작아도 에러 때문일 수 있으므로 feof로 끝인지 검사한다.
		if (result < 1 && feof(pReadFile))
		{
			break;
		}

		fwrite(&buf, elementSize, elementCount, pWriteFile); // 입력 스트림에서 가져온 데이터를 출력 스트림에 쓴다.
	}

	// 파일 스트림 정리
	fclose(pReadFile);
	fclose(pWriteFile);
}

파일 스트림 버퍼 비우기

// 리턴 : 성공 시 0, 실패 시 EOF 반환
fflush(file);

파일 스트림 닫기

fclose()

// 리턴 : 성공 시 0, 실패 시 EOF 반환
fclose(file);

텍스트와 바이너리 데이터를 파일에 저장하기

fprintf(), fscanf()

cout << "=== format file output" << endl;
{
	// open file stream
	FILE *file;
	fopen_s(&file, "formatFile.txt", "wt");

	// output text
	fprintf_s(file, "string: %s ,int : %d", "hihi", 99);

	// close file
	fclose(file);
}

cout << "=== format file input" << endl;
{
	// open file stream
	FILE *file;
	fopen_s(&file, "formatFile.txt", "rt");

	// input text
	char str[99];
	int num;
	// 스페이스 이전 문자까지 %s에 지정한 변수에 넣고 \n을 덧붙인다.
	fscanf_s(file, "string: %s ,int : %d", str, _countof(str)/*string의 크기*/, &num);

	cout << str << endl;
	cout << num << endl;

	fclose(file);
}

fwrite(), fread()

cout << "=== struct output" << endl;
{
	A a;
	a.a = 99;
	strcpy_s(a.b, sizeof(a.b), "abcd");

	FILE *file;
	fopen_s(&file, "structFile.bin", "wb");

	fwrite(&a, sizeof(a), 1, file);

	fclose(file);
}

cout << "=== struct input" << endl;
{
	A a = {};

	FILE *file;

	fopen_s(&file, "structFile.bin", "rb");
	fread_s(&a, sizeof(a), sizeof(a), 1, file);

	fclose(file);

	cout << a.a << endl;
	cout << a.b << endl;
}

파일 위치 지시자

fseek()

#include <stdio.h>
/*
FILE *stream : 파일 스트림
long offset : 이동 시킬 바이트 수
int wherefrom : 이동을 시작할 위치
*/
int fseek(FILE *stream, long offset, int wherefrom);
	cout << "=== fseek() output" << endl;
	{
		FILE *file = nullptr;
		fopen_s(&file, "fseek.txt", "wt");

		fputs("123456789", file);

		fclose(file);
	}

	cout << "=== fseek() input" << endl;
	{
		FILE *file = nullptr;
		fopen_s(&file, "fseek.txt", "rt");

		fseek(file, 2, SEEK_END);
		cout << fgetc(file) << endl; // -1 : 파일의 끝을 넘어서 이동했기 때문에 -1이 출력

		fseek(file, -2, SEEK_END);
		char c = fgetc(file);
		cout << c << endl; // 8 : 파일의 끝(eof)에서 2칸 뒤로 이동해서 8을 가리킨다.
		// fgetc()로 8을 출력한 후에는 9를 가리킨다.
		
		cout << fgetc(file) << endl; // 57 : 9에 해당하는 아스키코드
		// fgetc()는 아스키코드를 반환하기 때문에 바로 출력하면 숫자가 출력된다.
		// putchar(fgetc(file)); // putchar()는 아스키코드를 값을 받아 표준 출력 스트림에 출력한다.

		fseek(file, 2, SEEK_SET);
		putchar(fgetc(file)); // 3 : 파일의 처음(1)에서 2칸 이동해서 3을 지정한다.
		putchar('\n');
		// 3을 출력한 후에는 4를 가리킨다.

		fseek(file, 1, SEEK_CUR);
		putchar(fgetc(file)); // 5 : 현재 위치 4에서 한 칸이동하니 5를 가리킨다.
		putchar('\n');

		fclose(file);
	}

ftell()

#include <stdio.h>
/*
반환값 : 현재 파일 위치 지시자의 위치, 0부터 시작한다.
*/
long ftell(FILE *stream);
	// 파일 저장하고 부분적으로 변경하기
	cout << "=== ftell() output" << endl;
	{
		FILE *file = nullptr;
		fopen_s(&file, "ftell.txt", "wt");

		fputs("00100", file);

		fclose(file);
	}


	cout << "=== ftell() input" << endl;
	{
		FILE *file = nullptr;
		fopen_s(&file, "ftell.txt", "r+t"); // r+모드로 열어야 기존 내용을 유지할 수 있다.

		fseek(file, 2, SEEK_CUR); // 처음에서 2칸 이동으로 1을 가리킴
		int pos = ftell(file);
		cout << pos << endl; // 2 : 0부터 시작

		fputc('9', file); // 1을 9변경하여 저장
		fclose(file);
		file = nullptr;

		// 파일 다시 오픈
		fopen_s(&file, "ftell.txt", "rt");

		char str[10] = {};
		fgets(str, 10, file);
		fclose(file);

		cout << str << endl; // 00900
	}

헤더

파일 관련 C++ 클래스

경로

절대경로

상대경로

작업경로

파일 읽기 함수의 속도 비교

MFC에서 파일 복사

참고