본문 바로가기

언어/C언어

[C 언어] 12. File

반응형

File

fopen은 데이터를 송수신하기 위해 파일과 스트림을 생성합니다.

파일에 어떠한 짓을 하려면 open을 먼저 해주어야합니다.

open은 파일에 접근하기 위한 정보를 메모리에 올립니다.

FILE* fopen(cosnt char* filename, const char* mode)

파일의 오픈 모드는 파일 접근 모드와 데이터의 입출력 모드를 말합니다.

파일의 접근 모드는 다음과 같이 있습니다.

  • r : 읽기, 먼저 파일이 있어야 합니다.
  • w : 쓰기. 빈 파일을 생성합니다, 파일이 없으면 생성합니다.
  • a : 마지막에 이어 쓰기, 파일이 없으면 생성합니다.
  • r+ : 읽기, 쓰기, 파일이 있어야 합니다.
  • w+ : 읽기, 쓰기, 빈 파일 생성, 파일 없으면 생성
  • a+ : 읽기, 마지막에 쓰기, 파일 없으면 생성

데이터의 입출력 모드는 t(text mode), b(binary mode) 중 하나 입니다.

사용할때는 파일 접근 모드 바로 뒤에 연결해서 써줍니다.  ex) rt, rb, ...

text mode로 파일을 저장할때 스트림은 squence of lines로 간주합니다. 즉, 라인이 여러개 들어있는 것으로 간주합니다. 라인의 구분은 \n으로 구분합니다.

파일로 저장하면 newline의 표현은 운영체제마다 다릅니다.

C언어에서 newline : \n

Unix/Linux : \n

Mac OS X : \n(초기 classic Mac : \r)

Windows : \r\n

해당 운영채제의 파일을 만들 때 newline 변환이 필요합니다.

textmode는 이 변환을 자동으로 해줍니다.

binary mode는 아무런 변환도 없습니다.

t 또는 b를 적지 않으면 자동으로 t로 열립니다.

 

파일을 fopen 하면 반드시 fclose 해야 합니다.

파일을 open 하는 동안 OS는 다른 프로그램의 접근을 막습니다.

파일이 열리면 시스템에서 파일에 대한 lock를 걸어줍니다.

파일을 close하면 이 lock을 풉니다.

lock이 풀리면 그제서야 다른 프로그램에서 접근이 가능해집니다.

동시에 접근 된다면 파일이 뒤죽박죽되어 깨질 수도 있을 것입니다.

또한 닫지 않으면 시스템에 쓰레기가 남게되고 오류를 불러일으킬 수 있습니다.

파일을 닫는 코드는 다음과 같이 정의되어 있습니다.

int fclose(FILE* stream)

오류가 없다면 종료시 0을 반환합니다.

만약 fclose가 제대로 되지 않으면 운영체제 차원에서 해당 프로그램을 종료시켜야 합니다.

 

데이터의 입출력 모드 예시를 보겠습니다.

#include <stdio.h>
int main(void) {
	int state;
	FILE *file = fopen("test.txt", "wt");
	if (file == NULL) {
		printf("error\n");
		return 1;
	}
	fputs("abcd\n", file);
	state = fclose(file);
	if (state != 0) {
		printf("error\n");
		return 1;
	}
}

먼저 test.txt라는 파일을 w모드로 열었습니다.

그리고 file이라는 FILE 타입 포인터로 초기화했습니다.

file에 "abcd\n"를 입력했습니다.

그리고 파일을 닫았습니다.

정상적으로 닫혔다면 fclose는 0을 반환하기 때문에 state는 0이라는 값을 가질 겁니다.

실행결과를 살펴보겠습니다.

abcd는 각각 아스키코드로 61,62,63,64가 됩니다. 그런데 뒤에 0D와 0A가 있습니다.

\n은 어디로 사라지고 저 두개가 붙은것인지 알아보겠습니다.

위에서 설명했다시피  Windows에서 \n은 \r\n으로 바뀝니다. 또한 textmode이기 때문에 알아서 문자가 바뀐것입니다.

만약 운영체제가 Unix였다면 \n은 그대로 \n이였을 것입니다.

현재 운영체제가 Windows이기 때문에 0D 와 0A는 각각 \r과 \n을 의미합니다.


EOF

EOF는 파일의 끝이라는 함수들의 일종의 신호이며 약속입니다.

파일의 끝에 기호가 붙는것이 아닙니다.

파일을 읽는 함수들이 파일의 끝에 다다르면 EOF(-1)을 반환하는 것입니다.

만약 파일을 다 읽고 fgetc를 한다면 -1을 리턴할 것입니다.

EOF는 상태에 대한 정보를 가지고 있고 일종의 시그널입니다. 이것을 나타내는 값이 -1입니다.

fgetc의 반환형이 int인 이유는 char와 EOF를 둘다 표현해야 하기 때문입니다.

1바이트인 char형을 사용하면 코드번호가 255인것과 -1인 것의 구별이 불가능해집니다.

왜냐하면 1바이트에서는 두개의 이진수 표현이 모두 11111111이기 때문입니다.

 

일반적인 파일의 끝 검사를 살펴보겠습니다.

int ch;
ch = fgetc(file);
if (feof(file) != 0)
	break;
printf("data : %c \n", ch);

file포인터는 세 가지로 구성되어있습니다. position indicator, 버퍼, 상태지시자입니다.

함수 feof는 position indicator를 보고 EOF인지 확인하고 리턴합니다.


File Input/Output functions

출력

int fprintf(FILE* stream, const char* format [, argument]...)

화면에 출력하는 것이 아닌 파일(stream)로 형식을 갖춘 데이터를 출력합니다.

에러가 발생하면 음수를 반환합니다.

 

입력

int fscanf(FILE* stream, const char* format [, argument]...)

화면 대신 파일(stream)로부터 서식을 갖춘 데이터를 입력받습니다.

파일의 데이터를 모두 읽으면  EOF(-1)을 반환합니다.

 

예시를 보겠습니다.

#include <stdio.h>

void main() {
	FILE *in, *out;
	char inchar;
	printf("Input file : before.txt\n");
	printf("Output file : after.txt\n");
	in = fopen("before.txt", "r"); // read only mode
	out = fopen("after.txt", "w"); // write only mode
	while ((fscanf(in, "%c", &inchar)) != EOF)
		fprintf(out, "%c", inchar);
	printf(“Text copy success.\n");
	fclose(in);
	fclose(out);
}

여기서 before.txt는 먼저 생성되어 있어야합니다.

before에 있는 파일이 after에 복사될 것입니다.

 

파일의 읽기와 쓰기의 위치를 이동시킬 수도 있습니다.

fseek 함수를 이용합니다.

int fseek(FILE* stream, long offset, int origin)

여기서 origin에는  3가지를 쓸 수 있습니다.

  • SEEK_SET : 파일의 맨 앞
  • SEEK_CUR : 현재 위치
  • SEEK_END : 파일의 맨 끝

offset은 origin에서 이동하기 원하는 바이트 수를 말합니다.

offset이  양수면 오른쪽으로 이동 음수면 왼쪽으로 이동합니다.

 

예시를 보겠습니다.

#include <stdio.h>
void main(void) {
	char buf[10];
	FILE * file = fopen("Test.txt", "wt");
	fputs("123456789abcde", file);
	fclose(file);
	file = fopen("Test.txt", "rt");
	fgets(buf, 7, file);
	printf("%s \n", buf);

	//fseek(file, 2, SEEK_CUR);
	//fseek(file, -2, SEEK_CUR);
	//fseek(file, 2, SEEK_SET);
	//fseek(file, -2, SEEK_END);
	printf("%c \n", fgetc(file));
	fclose(file);
}

현재 fgets(buf, 7, file);로 했기 때문에 6번 인덱스까지 읽고 7번 인덱스에 position indicator가 있을 것입니다.

fseek을 아래와 같이 한다면 7번 인덱스에서 2 증가해서 9번 인덱스가 됩니다.

다시 9번 인덱스에서 2 감소하여 7번 인덱스가 됩니다.

파일의 맨 처음으로 가서 2 증가하여 2번 인덱스가 됩니다.

파일의 맨 끝으로 이동하여 2 감소하여 7번 인덱스가 됩니다.


Block Input/Output

바이너리 데이터 또는 블록 단위의 텍스트를 읽거나 쓰기 위한 함수들을 말합니다.

읽는 함수는 다음과 같습니다.

size_t fread(void* data, size_t size, size_count, FILE* stream)

파일 포인터 stream에서data 배열로 데이터를 read합니다.

void* data는 타입이 void 포인터 입니다.

타입이 void라는 것은 값이 없는 것이 아니라 타입을 미지정했다는 의미입니다.

따라서 int, double, ...  어떤 것도 타입으로 지정될 수 있습니다.

size_t size와 size_t count는얼마나 읽을 것인지에 대한 정보입니다.

size는 읽어올 원소의 바이트 크기, count는 읽어올 원소의 개수입니다.

읽기에 성공한 원소의 개수를 리턴하거나 size와 다른경우 error 또는 EOF를 반환합니다.

 

쓰기 함수는 다음과 같습니다.

size_t fwrite(cosnt void* data, size_t size, size_t count, FILE* stream)

data 배열의 값을 파일 포인터 stream에 write합니다.

size는 쓸 원소의 바이트 크기, count는 쓸 원소의 개수를 말합니다.

쓰기에 성공한 원소의 개수를 리턴합니다. size 보다 작으면 error입니다.

 

예시 코드를 보겠습니다.

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

int main() {
   FILE *fp;
   char c[] = "Block Input/Output example:";
   char buffer[100];
   fp = fopen("file.txt", "w+"); 
   fwrite(c, strlen(c) + 1, 1, fp); 
   fseek(fp, 0, SEEK_SET); 
   fread(buffer, strlen(c)+1, 1, fp);
   printf("%s\n", buffer);
   fclose(fp);
   return(0);
}

 

반응형

'언어 > C언어' 카테고리의 다른 글

[C 언어] 13. 구조체  (0) 2024.04.14
[C 언어] 11. Input/Output Stream, String  (0) 2024.04.14
[C 언어] 10.포인터  (0) 2024.04.14
[C 언어] 9. 함수  (0) 2024.04.14
[C 언어] 8. 배열  (0) 2024.04.14