Developer

28.(C언어) 파일 입출력 본문

Programming Language/C

28.(C언어) 파일 입출력

DPhater 2020. 8. 1. 22:26

지금까지는 키보드로 입력받고, 화면으로 출력만 해보았다. 이제 파일로 부터 읽어오고, 파일로 출력하는 C언어의 파일 입출력을 배워보자. 파일에 입출력을 하기 위해서는 파일 포인터를 선언해 fopen함수로 원하는 파일의 포인터를 얻어야 한다.

FILE *fp =fopen("test.txt","w");

위의 코드는 test.txt 파일을 w(쓰기)모드로 열어 파일포인터 fp에 저장한 것이다.

fopen함수는 파일 열기에 시작하면 파일 포인터를 반환하고, 실패하면 NULL을 반환한다.

파일 모드

기능

기능 설명

"r"

읽기 전용

파일을 읽기 전용으로 연다. 반드시 파일이 있어야 함

"w"

쓰기 전용

파일을 쓰기 전용으로 연다. 파일이 없다면 파일을 생성하고, 있다면 내용을 덮어쓴다.

"a"

추가

파일이 있다면 기존파일의 끝에 추가한다. 만약 없다면 파일을 생성한다.

"r+"

읽기/쓰기

파일을 읽기/쓰기용으로 연다. 파일이 없다면 NULL을 반한한다.

"w+"

일기/쓰기

파일을 읽기/쓰기용으로 연다. 파일이 없다면 생성하고, 있다면 덮어쓴다.

"a+"

추가(읽기/쓰기)

파일이 없다면 생성한다. 있다면 모든 구간에서 읽을 수 있다. 쓰기는 끝에서만 가능하다.

위의 표를 보면 fopen을 사용할 때 사용가능한 파일 모드 종류를 알 수 있다.

위의 모드 이외에도 t와 b라는 모드가 있다. t는 텍스트 모드를 말하는데 텍스트 모드는 숫자를 저장해도 문자열 형식으로 저장한다.그리고 \n을 \r\n로 변환해 저장하며 읽을 때에는 \r\n을 \n으로 변환해 읽어온다. b는 바이너리 모드를 말하며 숫자를 저장할 경우 숫자 그대로 저장한다.

t와b는 단독으로 사용할 수 없고 표의 6가지와 조합해 사용해야한다.

(ex> "rt" ,"ab" )

fopen으로 파일을 열어 사용했다면 사용이 끝난 파일 포인터는 fclose를 사용해 닫아줘야 한다.

#include<stdio.h>

int main(){
	FILE *fp=fopen("test.txt","w");  //파일열기 (쓰기모드) 
	fprintf(fp,"%s\n","Hello World!"); //파일에 쓰기 
	fclose(fp);   //파일 닫기 
	return 0;
}

코드1 실행 후 test.txt파일

콘솔(화면)에 출력을 할때에는 printf함수를 사용했지만 파일에 출력할 경우 fprintf를 사용한다. 파일 포인터를 전달인자로 주는것만 다르고 다른 사용법은 똑같다. 만약 파일 포인터 인자를 작성하는 곳에 stdout를 작성하면 화면으로 출력을 할 수 있다. 코드1처럼 "test.txt"로 파일명을 정해주면 현재 디렉토리에서 test.txt파일을 사용하거나 생성한다. 반드시 현재 디렉토리에 있어야 하는것은 아니고 절대 경로나 상대 경로를 작성해도 된다.

#include<stdio.h>

int main(){
	char str[20];
	FILE *fp=fopen("test.txt","r");  //파일열기 (읽기모드) 
	fscanf(fp,"%s",str); //파일에서 문자열을 str로 읽기
	printf("%s\n",str);
	fclose(fp);   //파일 닫기 
	return 0;
}

코드2 실행 결과

fsacnf를 사용해 test.txt에서 문자열을 읽어 str에 저장했다. 분명 파일에는

Hello World!가 들어 있지만 fscanf로 읽어온 문자열은 Hello뿐이다. fscanf는 공백과 개행을 기준으로 읽어오기 때문이다. 이러한 경우 문자열 전용 입출력 합수를 사용하면 된다.

fputs, fgets

#include<stdio.h>

int main(){
	FILE *fp=fopen("test1.txt","w");
	fputs("Hello World!!!!",fp);
	fclose(fp);
	return 0;
}

코드3 실행 후 파일 상태

fputs는 문자열을 그대로 파일에 쓸 수 있다. fputs는 파일 포인터를 두 번째 인자로 넘겨준다.

#include<stdio.h>
int main(){
	char s[20];
	FILE *fp=fopen("test1.txt","r");
	fgets(s,sizeof(s),fp);
	printf("%s\n",s);
	fclose(fp);
	return 0;
}

코드4 실행 결과

fputs로 문자열을 파일에 썼다면, fgets로 파일로부터 문자열을 읽어올 수 있다. fgets는 \n까지 읽어온다.

fwrite, fread

fwrite는 4개의 전달인자를 받는다.

작성 하려는 배열, 원소의 사이즈(byte), 원소의 갯수, 파일 포인터 이렇게 4개의 전달인자가 필요하다.

#include<stdio.h>
int main(){
	FILE *fp=fopen("test3.txt","w");
	fwrite("Hello Hello World!!",1,19,fp);
	fclose(fp);
	return 0;
}

코드5 실행 후 파일 상태

char형이므로 2번째 인자로 1, 작성하려는 원소 갯수 19를 3번째 인자로 작성 하였다. 만약 3번째 인자로 10을 작성했다면 Hello Hell까지 작성 되었을 것이다.

원하는 만큼 파일로부터 읽기위한 fread함수도 존재한다.

fread함수도 4개의 인자를 받는다. 첫 인자는 파일에서 읽은것을 저장할 배열이고 나머지 인자는 fwrite함수와 같다. 읽은 크기만큼 반환한다.

#include<stdio.h>
int main(){
	char str[20]={0,};  //모든공간 NULL로 초기화 
	FILE *fp=fopen("test3.txt","r");
	fread(str,1,19,fp);
	printf("%s\n",str);
	fclose(fp);
	return 0;
}

코드6 실행 결과

fread는 \n이 있던지 없던지 무조건 지정된 크기만큼 읽어온다. 그리고 C에서는 문자열의 끝에 NULL이 들어있지만 파일에 저장된 문자는 NULL이 들어가 있지 않는다. 그래서 str을 NULL로 초기화 하지 않으면 쓸대없는 값이 출력될 수 있다.

파일 포인터 위치 변경

fseek함수, ftell함수, rewind함수의,feof함수 사용법을 알아보자.

fseek함수의 원형은 다음과 같다.

fseek(파일포인터, 이동할크기,기준점);

파일 포인터의 위치를 기준점 부터 이동할 크기만큼 이동시킨다.

기준점은 다음 표와 같이 3개가 있다.

SEEK_SET

파일의 처음

fseek(fp,0,SEEK_SET)

- 파일 포인터를 파일의 처음으로 이동

SEEK_CUR

현재 위치터

fseek(fp,5,SEEK_CUR)

-파일 포인터를 현재 위치에서 5만큼 이동

SEEK_END

파일의 끝

fseek(fp,-5,SEEK_END)

-파일 포인터를 파일의 끝에서 역방향으로 5만큼 이동

ftell함수와 rewind함수,feof함수 사용은 다음과 같이 하면된다.

ftell(파일포인터);

파일 포인터의 현재 위치를 반환한다.

rewind(파일포인터);

파일 포인터를 파일의 처음으로 이동시킨다.

feof(파일포인터);

파일의 끝이면1, 아니면 0 반환한다.

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

int main(){
	char str[5]={0,}; //NULL로 초기화
	int tot=0;        //읽어온 크기 누적할 변수 
	FILE *fp=fopen("test3.txt","r");
	while(feof(fp)==0){ //파일 끝가지 반복 
		tot+=fread(str,sizeof(char),4,fp); //4byte읽어서 str에 저장 
		printf("%s",str);  //str출력
		memset(str,0,5) ;
	}
	printf("\nfile size: %d\n",tot);
	
	//현재 파일포인터는 파일 끝에 위치 
	fseek(fp,3,SEEK_SET) ; //처음 위치에서 3번째 오른쪽으로 이동 
	while(feof(fp)==0){
		fread(str,sizeof(char),4,fp);
		printf("%s",str);
		memset(str,0,5);
	}printf("\n");
	
	fclose(fp);
	return 0;
}

코드7 실행 결과

5btye의 버퍼로 19byte의 파일을 모두 읽어서 출력한 프로그램이다.

처음while문에서 파일 포인터가 파일의 끝에 도달할때까지 4byte씩 읽어 출력한다.

두번째 while문은 fseek를 사용한 예를 보여주기위해 작성하였다. fseek를 사용해 파일 포인터의 위치를 파일의 시작에서 3byte위치로 이동시켰다. 그런 후 다시 출력해 lo Hello World!!가 출력된걸 볼 수 있다.

구조체

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

#pragma pack(push,1)
typedef struct data{
	char c;
	short sh;
	int i;
	char s[20];
}data;
#pragma pack(pop)

int main(){
	data d;
	FILE *fp=fopen("data.bin","wb");
	d.c='t';
	d.sh=32;
	d.i=50000;
    strcpy(d.s,"test strcut");
    fwrite(&d,1,sizeof(d),fp);
    fclose(fp);
    return 0;
}

코드8 실행 결과

구조체를 각 변수의 자료형 크기만큼 공간을 차지하도록 하게하기 위해 1바이트 크기로 정렬하였다.

바이너리 읽기모드로 파일을 열었다. 위에서도 말했지만 바이너리 형식은 변환 없이 저장되어 읽고,쓸때 편하다. 그런 후 구조체의 멤버 변수들에게 값을 넣어준 뒤

frwrite를 사용해 파일에 구조체 크기만큼 출력하였다. 이때 구조체 변수d는 포인터 형식이 아니므로 &d로 작성하였다.

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

#pragma pack(push,1)
typedef struct data{
	char c;
	short sh;
	int i;
	char s[20];
}data;
#pragma pack(pop)

int main(){
	data d;
	FILE *fp=fopen("data.bin","rb");
    fread(&d,1,sizeof(d),fp);
    printf("%c  %d  %d  %s\n",d.c,d.sh,d.i,d.s);
    fclose(fp);
    return 0;
}

코드9 실행 결과

읽는것 또 한 같은 방식으로 가능하다. 주의할 점은 구조체를 1byte로 정렬해서 사용해야하며 파일에 출력한 구조체의 멤버변수 순서와 입력받을 구조체의 멤버변수 순서가 같아야한다.

'Programming Language > C' 카테고리의 다른 글

27.(C언어) 함수(3)  (0) 2020.08.01
26.(C언어) 함수(2)  (0) 2020.08.01
25.(C언어) 함수(1)  (0) 2020.08.01
24.(C언어) 지역변수와 전역변수  (0) 2020.08.01
23.(C언어) 공용체, 열거형  (0) 2020.08.01
Comments