본문 바로가기
카테고리 없음

C 언어 고급편]🚀18 파일 입출력

by redcubes 2024. 4. 13.

목차

파일 개방과 입출력

파일 개방과 폐쇄

#include <stdio.h>

int main(void)
{
	FILE *fp;
	fp = fopen("a.txt","r");
	if (fp == NULL)
	{
		printf("파일이 열리지 않았습니다.\n");
		return 1;
	}
	printf("파일이 열렸습니다.\n");
	fclose(fp);
	return 0;
}

fopen 함수 원형
FILE *fopen(const char *, const char *);
기본 위치는 현재 디렉터리 == 실행 파일이 있는 위치 절대경로를 주고 싶다면
fopen(“c:\source\a.txt”, “r”)
백슬래시는 이스케이프를 위해 두 번씩 쓴다.
현재 작업 디랙터리를 기준으로 상대 경로 지정도 가능.
"…"상위 폴더
개방 모드는 r,w,a
파일이 없으면
r은 NULL널 포인터 반환. 나머지는 새로운 파일을 생성함.
fopen함수는 실제 파일이 있는 장치와 연결되는 스트림 파일을 메모리에 만듦. 그리고 스트림 파일에 접근할 수 있게 파일 포인터를 반환함. => 이 포인터를 가지면 입출력 함수로 원하는 입출력 수행 가능.
NULL은 stdio.h에
#define NULL ((void *)0) 으로 정의됨
포인터 반환하는 함수에서 exception을 알리기 위해 사용됨. 그래서 fopen사용 뒤에는 반드시 리턴을 검사해서 파일이 정상적으로 열렸는지 확인해야 함.

if (fp == NULL)
{
	print("파일이 열리지 않았습니다.\n");
	return 1;
}

w,a는 파일이 없어도 문제가 없지만. 개방 가능한 파일 수를 넘기거나 기록장치에 문제가 생기는 등의 exception이 발생할 수 있으므로 항상 개방 여부를 검사.

fclose 함수 원형
int fclose(FILE *);
닫을 파일의 포인터를 줌. 성공적으로 닫으면 0을 반환하고 오류가 발생하면 EOF반환.
stdio.h에 #define EOF (-1)로 정의
스트림 파일에 할당된 메모리를 회수해 재활용.
사용이 끝나면 메모리에서 사고로 지워지기 전에 바로 닫아야 스트림 파일이 장치에 기록됨.

스트림 파일과 파일 포인터

스트림 파일은 문자열 포인터의 형태의 버퍼를 가지고 있음.
버퍼를 사용하기 위한 정보(데이터 위치, 버퍼의 메모리 위치, 크기)를 저장하는 파일 구조체의 이름이 바로 FILE임.
fopen이 메모리에 스트림 파일 만들고 프로그램에서 사용할 수 있게 구조체 변수의 주소를 반환. 이 값을 사용해서 스트림 파일을 통해 파일 입출력 수행.

컴파일러에 따라 포함한 정보에 차이는 있음.

스트림 파일 사용의 장점

  1. 입출력 효율 향상, 장치 독립적 프로그래밍.
  2. 프로그램과 장치의 입출력 속도 차이 줄이기

문자 입력 함수: fgetc

파일이 개방되면 실질적인 데이터 입출력은 함수를 통해서 이루어짐.
fgetc는 파일에서 문자 하나를 입력해 반환.
파일의 데이터를 모두 읽으면 EOF반환

  1. 함수가 파일 포인터와 연결된 스트림 파일의 버퍼에서 데이터 가져옴.
  2. 처음에는 버퍼가 비어 있으므로 저장 장치에서 데이터를 가져와서 버퍼를 채우는데 이때 읽는 데이터의 크기에 주목.
  3. 한 번에 버퍼 크기만큼 가져와서 저장.
  4. 함수는 버퍼에서 첫 문자 가져와서 반환.
  5. 두 번째 호출부터는 버퍼에서 바로 문자 읽음. 자동으로 두 번째 문자 읽음.(위치 지시자 때문에 가능)
  6. 위치 지시자는 파일 개방시 0으로 초기화되고 입력 함수가 데이터를 읽을 때마다 읽은 크기만큼씩 증가.
  7. 더 없으면 EOF반환

문자 출력 함수: fputc

출력할 문자와 파일 포인터를 주면 파일로 문자를 출력. 리턴은 출력한 문자(성공시) 실패하면 EOF
이 함수도 스트림 파일의 버퍼를 사용.
버퍼에 모은 뒤 한 번에 출력.
버퍼가 꽉 차거나, 개행 문자가 출력되거나.
혹은 시스템의 규칙에 따라 파일에 기록.
만약에 버퍼의 데이터를 즉시 장치로 출력해야 한다면 fflush함수사용.

기본적으로 개방되는 표준 입출력 스트림 파일

운영체제는 기본 3개의 스트림 파일을 프로그램 실행시 개방. 키보드 모니터 등에 연결해서 입출력 함수들이 파일 포인터 없이 사용할 수 있게 제공함.
기본 공통 스트림은
stdin - 표준 입력 스트림 - 키보드
stdout - 표준 출력 스트림- 모니터
stderr - 표준 에러 스트림 - 모니터
각 이름은 운영체제가 개방한 스트림 파일의 주소를 나타냄.
getchar함수는 내부적으로 stdin사용.
호출되면 키보드 입력 데이터가 개행 문자와 함께 stdin 스트림 파일의 버퍼에 한꺼번에 저장. 그리고 버퍼에서 첫 문자 가져다 반환.
두 번째 호출부터는 버퍼에서 바로 가져다 반환.(== 키보드로는 한 번 치지만 함수는 여러번 호출됨.)
Ctrl+Z입력하면 함수가 EOF를 반환해 프로그램 종료.
^Z EOF문자. 키보드 입력이 끝남을 의미.
유닉스나 리눅스에서는 Ctrl+D

운영체제 기본 개방 스트림 파일을 사용하는 함수들: scanf, printf, getchar, gets, puts 등
파일 포인터를 인수로 받는 함수들도 인수로 stdin, stdout, stderr을 직접 인수로 제공하면 운영체제 기본 개방 스트림파일을 사용할 수 있음.

텍스트 파일과 바이너리 파일

텍스트 파일: 아스키 코드
바이너리: 그 외.

개방 모드에 텍스트 파일은 t
바이너리는 b 추가해 개방.
rb, wb, ab 등
명시 안하면 기본으로 텍스트 파일

파일의 형태와 개방 모드가 다르면 심각한 문제 발생!!
파일을 다 못 읽어올 수도 있음.

+개방 모드, fseek, rewind, feof 함수

r+ : 텍스트 파일 읽고 쓰기
w+ :텍스트 파일 내용 지우고 읽거나 쓰기
a+ : 텍스트 파일 읽거나 끝에 추가
rb+: 바이너리 읽고 쓰기
wb+ : 바이너리 지우고 읽고 쓰기
ab+: 바이너리 읽거나 파일 끝에 추가

fseek함수 원형 int fseek(FILE * stream, long offset, int whence);

stream파일의 버퍼에서 `whence`를 기준으로 `offset`만큼 위치 지시자를 옮긴다.
위치 이동에 실패하면 0, 성공하면 0이 아닌 값 반환.

`whence`에 사용 가능한 값과 의미

매크로명
(전처리시 정수로 변환)
기준 위치 오프셋 값
0 SEEK_SET 파일의 처음 양수만 가능
1 SEEK_CUR 파일의 현재 위치 양수와 음수 모두 가능
2 SEEK_END 파일의 끝 음수만 가능

rewind함수는 위치 지시자를 맨 처음으로 설정한다.

a+ 모드는 어떤 경우든 항상 파일의 맨 뒤에 붙여넣기.

w+ 모드는 데이터를 읽다가 중간에 다시 쓰는 경우 fseek함수로 설정한 위치부터 내용을 덮어 씀.

r+모드는 먼저 읽든 쓰든 상관이 없지만 . 전환 시 fseek함수로 읽고 쓸 위치 알려줘야 함.

파일을 열자마자 출력하면 앞부터 덮어 쓰며, 다읽고 출력하면 fseek호출 없이 이어쓸 수 있음.

 

feof

스트림 파일의 데이터를 모두 읽었는지 확인 할 때 유용. 끝이면 0아닌 값. 끝이 아니면 0반환

입력 함수가 데이터 입력에 실패한 이후에 그 결과를 알 수 있어 입력함수 다음에 사용.

 

 

다양한 파일 입출력 함수

fgets fscanf fprintf fflush fread fwrite
파일의 데이터는 fgetc로 모두 읽고, fputc로 모두 쓸 수 있지만.
저 편리하게…한 줄씩 혹은 문자인 파일을 정수나 실수 등으로 자동 변환하거나.

fgets fputs 한 줄씩 입출력

fgets(str, sizeof(str), ifp);
공백 포함 한 줄씩 읽어 배열에 저장
배열보다 한 줄이 크면 배열 크기까지만 저장.(배열길이-1, \0때문에)
개행 문자까지 입력. ~개행문자 + 널문자 로 끝남.
입력된 문자열의 길이에서 1을 뺀 값을 이용해서 개핼 문잘을 제거
입력 파일의 형식에 주의. 원래 파일에 개행 문자가 없을 수도 있음.(마지막 문자가 지워짐.)
입력을 마치면 입력한 배열의 주소를 다시 반환- 호출 후 바로 입력된 데이터 사용 가능.하지만 주솔르 반환하므로 파일을 다 읽었을 때의 반환값에 주의.
더이상 읽을 데이터가 없으면 NULL반환. EOF(-1)로 착각하지 않게 주의.
fputs(출력할 문자열, 파일 포인터);
입력할 저장 공간의 크기를 인수로 줄 수 있어서 안전하므로 콘솔 입출력도 이 함수들을 활용하면 좋음.

fscanf fprintf 다양한 형태로 입출력

#include <stdio.h>

int main(void)
{
	FILE* ifp, * ofp;				// 파일 포인터 선언
	char name[20];					// 이름
	int kor, eng, math;				// 세 과목 점수
	int total;						// 총점
	double avg;						// 평균
	int res;						// fscanf 함수의 반환값 저장

	ifp = fopen("a.txt", "r");      // 입력 파일을 읽기 전용으로 개방
	if (ifp == NULL)                // 개방 여부 확인
	{
		printf("입력 파일을 열지 못했습니다.\n");
		return 1;
	}

	ofp = fopen("b.txt", "w");      // 출력 파일을 쓰기 전용으로 개방
	if (ofp == NULL)                // 개방 여부 확인
	{
		printf("출력 파일을 열지 못했습니다.\n");
		return 1;
	}

	while (1)
	{
		res = fscanf(ifp, "%s%d%d%d", name, &kor, &eng, &math);  // 데이터 입력
		if (res == EOF)             // 파일의 데이터를 모두 읽으면 EOF 반환
		{
			break;
		}
		total = kor + eng + math;   // 총점 계산
		avg = total / 3.0;          // 평균 계산
		fprintf(ofp, "%s%5d%7.1lf\n", name, total, avg);   // 이름, 총점, 평균 출력
	}

	fclose(ifp);                    // 입력 파일 닫기
	fclose(ofp);                    // 출력 파일 닫기

	return 0;
}

fscanf로 스플릿을 쉽게 할 수 있음.

스트림 파일의 버퍼 공유 문제와 fflush 함수

int fflush(FILE *);
스트림 파일의 버퍼를 비우고,0 반환. 못 비우면 EOF
출력 파일에 대해 사용.

fread, fwirite

입출력할 데이터의 크기와 개수를 인수로 줄 수 있음.
구조체나 배열과 같이 데이터 양이 많은 경우에도 파일에 쉽게 입출력 가능.
숫자와 문자 변환 하지 않아 입출력 효율 높임.
(메모장에서 확인 불가.)

fread 함수는 파일로부터 데이터를 읽어오는 함수입니다. 이 함수의 원형은 다음과 같습니다.

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

매개변수

  • ptr: 데이터를 저장할 메모리 포인터
  • size: 각 데이터 항목의 크기 (바이트 단위)
  • nmemb: 읽을 데이터 항목의 개수
  • stream: 읽을 파일에 대한 포인터

반환 값

fread는 실제로 읽은 항목의 개수를 반환합니다. 이 값은 요청한 항목 수보다 작을 수 있으며, 파일 끝(EOF)에 도달하거나 읽기 에러가 발생했을 때 발생합니다.

사용 예

FILE *file = fopen("example.dat", "rb");
if (file) {
    char buffer[100];
    size_t result = fread(buffer, sizeof(char), 100, file);
    fclose(file);
}

fwrite 함수는 데이터를 파일에 쓰는 함수입니다. 이 함수의 원형은 다음과 같습니다.

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

매개변수

  • ptr: 파일에 쓸 데이터의 메모리 포인터
  • size: 각 데이터 항목의 크기 (바이트 단위)
  • nmemb: 쓸 데이터 항목의 개수
  • stream: 쓸 파일에 대한 포인터

반환 값

fwrite는 실제로 쓴 항목의 개수를 반환합니다. 이 값은 요청한 항목 수와 동일해야 정상적으로 데이터가 파일에 쓰여진 것입니다.

사용 예

FILE *file = fopen("example.dat", "wb");
if (file) {
    char data[100] = "Hello, World!";
    fwrite(data, sizeof(char), strlen(data), file);
    fclose(file);
}