7. C 표준 파일 입출력

김동웅·2021년 9월 25일
0

UNIX

목록 보기
8/14

핵심개념

• 파일은 모든 데이터를 연속된 바이트 형태로 저장한다.

• 파일을 사용하기 위해서는 반드시 파일 열기 fopen()를 먼저 해야 하며 파일 열기를 하면 FILE 구조체에 대한 포인터가 리턴된다.

• FILE 포인터는 열린 파일을 나타낸다.

• fgetc() 함수와 fputc() 함수를 사용하여 파일에 문자 단위 입출력을 할 수 있다.

• fgets() 함수와 fputs() 함수를 이용하여 텍스트 파일에서 한 줄씩 읽거나 쓸 수 있다.

• fread()와 fwrite() 함수는 한 번에 일정한 크기의 데이터를 파일에 읽거나 쓴다.

• 열린 파일에서 다음 읽거나 쓸 파일 내 위치를 현재 파일 위치라고 하며 파일 위치 포인터가 그 파일의 현재 파일 위치를 가리키고 있다.

• fseek() 함수는 현재 파일 위치를 지정한 위치로 이동시킨다.


7.1 파일 및 파일 포인터

시스템 호출

  • Unix 커널에 서비스를 요청을 위한 프로그래밍 인터페이스
  • C함수 처럼 호출될 수 있다.
  • 응용프로그램은 시스템 호출을 통해서 커널에 서비스를 요청한다.

C 라이브러리 함수

  • C 라이브러리 함수는 보통 시스템 호출을 포장해놓은 함수
  • 보통 내부에서 시스템 호출을 한다.

파일

  • C 프로그램에서 파일이 필요한 이유
    : 변수에 저장된 정보들은 실행이 끝나면 모두 사리지지만, 파일에 기록하면 정보를 영구적으로 저장할수 있기 때문

  • 유닉스 파일
    : 모든 데이터를 연속된 바이트 형태로 저장한다.

C언어의 파일 종류

  1. 텍스트 파일
  • 사람들이 읽을 수 있는 문자들을 저장 하고있는 파일
  • 텍스트 파일에서 "한줄의 끝" 을 나타내는 표현은 파일이 읽어 들여질 때, C내부의 방식으로 변환된다.
  1. 이진 파일
  • 모든 데이터를 있는 그대로 즉, 바이트의 연속으로 저장
  • 이진 파일을 이용하여 메모리에 저장된 변수 값 형태 그대로 파일에 저장할 수 있다.

파일 입출력

  • C언어의 파일 입출력 과정
  1. 파일 열기 : fopen()함수 사용
    -> open() 시스템호출
  • 파일을 사용하기 위해서 파일 열기를 해야함.
  • 파일열기를하면 FILE 구조체에 대한 포인터가 리턴되며 FILE포인터는 열린파일을 나타낸다.
  • FILE 구조체 : 하나의 스트림에 대한 정보를 포함하는 구조체로 버퍼에 대한 포인터, 버퍼크기.. 등등 열린 파일의 현재 상태를 나타내는 필드 변수들을 가지고 있다.
  • 함수 fopen() : FILE fopen(const char filename, const char mode);
    : const char
    filnename : 파일명에 대한포인터
    : const char* mode : 파일 입출력 모드

ex)

FILE *fp;
fp = fopen("~/sp/text.txt","r");
if(fp == NULL)
{
	printf("파일 열기 오류\n");
}
  1. 파일 입출력 : 다양한 파일 입출력 함수 사용

  2. 파일 닫기 : fclose() 사용
    -> close() 시스템호출
    닫기에 성공하면 0, 오류일 때는 EOF( -1)를 리턴함.

실행방법 : fclose(fp);


7.2 텍스트 파일

파일 입출력 함수

  • fgetc() 함수 : 파일에 문자단위 입력
    fputc() 함수 : 파일에 문자단위 출력

  • int fgetc(FILE *fp)
    - getc,fgetc 함수는 fp가 지정한 파일에서 한 문자를 읽어서 리턴

    • 파일끝에 도달하면 EOF(-1)리턴
  • int fputc(int c, FILE *fp)
    - putc,fputc 함수는 파일에 한 문자씩 출력하는 함수

    • 리턴값으로 출력하는 문자 리턴
    • 출력시 오류가 발생하면 EOF(-1)리턴

ex1) cat.c

#include <stdio.h>

/* 텍스트 파일 내용을 표준출력에 프린트 */

int main(int argc, char *argv[])
{

	FILE *fp;
	int c;

	if (argc < 2) 
	fp = stdin; // 명령줄 인수가 없으면 표준입력 사용

	else fp = fopen(argv[1],"r"); // 읽기 전용으로 파일 열기

	c = getc(fp); // 파일로부터 문자 읽기

	while (c != EOF)
	{
        putc(c, stdout); // 읽은 문자를 표준출력에 출력
	c = getc(fp); // 파일로부터 문자 읽기
	}
    
	fclose(fp);
	return 0;
}

ex2) copy.c

#include <stdio.h>

int main(int argc, char* argv[])
{
	char c;
	FILE *fp1, *fp2;

	if (argc!=3)
	{
	fprintf(stderr,"How to use : %s FILE1 FILE2\n",argv[0]);
	return 1;
	}
	
    	// 첫번째 파일 읽기모드로 오픈
	fp1= fopen(argv[1],"r");
	
   	 // 파일열기 실패시 오류메시지 출력
	if(fp1 == NULL)
	{
		fprintf(stderr,"Open %s  error!\n",argv[1]);
		return 2;
	}

	// 두번째 파일 쓰기모드로 오픈
	fp2 = fopen(argv[2], "w");
    
   	// 내용복사 
	while((c= fgetc(fp1))!=EOF)
	{
		fputc(c,fp2);
	}

	fclose(fp1);
	fclose(fp2);

	return 0;
}
  • 기타 파일 관련 함수
  1. int feof(FILE *fp)
    : 파일 포인터 fp 가 파일의 끝을 탐지하면 0이 아닌 값을 리턴하고 파일 끝이면 0리턴

  2. int fflush(FILE *fp)
    : 아직 기록되지 않고 버퍼에 남아있는 데이터를 fp가 가리키는 출력파일에 보낸다.
    버퍼 비우기 기능 수행함수

줄 단위 입출력

  • fgets() 함수와 fputs() 함수
    : 텍스트 파일에서 한 줄씩 읽거나 쓸 수 있다.

ex) line.c

#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 80

int main(int argc, char* argv[])
{
	FILE *fp;
	int line = 0;
	char buffer[MAXLINE];

	if(argc!=2)
	{
		fprintf(stderr, "사용법:line 파일이름\n");

		exit(1);			
	}

	if( (fp = fopen(argv[1],"r")) ==NULL)
	{
		fprintf(stderr, "파일 열기 error \n");
		exit(2);
	}

	while(fgets(buffer,MAXLINE,fp)!=NULL)
	{
		line++;
		printf("%3d %s", line,buffer);
	}


	fclose(fp);
	return 0;
}

포맷 입출력

  • fprintf() 함수
    : printf() 함수와 같은 방법으로 파일에 데이터를 출력 할 수 있다.

  • fscanf() 함수
    : scanf() 함수와 같은 방법으로 파일로부터 데이터를 읽어 들일 수 있다.

ex1) fprintf.c

#include <stdio.h>
#include "student.h"
/* 학생 정보를 읽어 텍스트 파일에 저장한다. */
int main(int argc, char* argv[])
{
	struct student rec;
	FILE* fp;
	
	if (argc != 2) {
	fprintf(stderr, "사용법: %s 파일이름\n", argv[0]);
	return 1; 
	}
	
    	// argv[1]를 쓰기모드로 오픈 
	fp = fopen(argv[1],"w");
	
	printf("%-9s %-7s %-4s\n","학번", "이름", "점수");
	
    	// 학번 이름 점수 입력받고
	while(scanf("%d %s %d", &rec.id, rec.name, &rec.score)==3)
	// fp에 저장 
     	   fprintf(fp,"%10d %6s %6d\n",rec.id,rec.name,rec.score);
	
        fclose(fp);
	return 0;
}

ex2 ) fscan.c

#include <stdio.h> 
#include "student.h"
/* 텍스트 파일에서 학생 정보를 읽어 프린트한다. */ 

int main(int argc, char* argv[]) { 

struct student rec;
FILE *fp;

if (argc != 2) {
fprintf(stderr, "사용법: %s 파일이름\n", argv[0]);
return 1; 
}

fp = fopen(argv[1], "r");
printf("%-9s %-7s %-4s\n", "학번", "이름", "점수"); 

while (fscanf(fp,"%d %s %d", &rec.id, rec.name, &rec.score)==3) 
printf("%10d %6s %6d\n", rec.id, rec.name, rec.score);

fclose(fp);
return 0;
}


7.3 이진 파일

이진 파일은

  • 모든 데이터를 있는 그대로 즉, 바이트의 연속으로 저장
  • 이진 파일을 이용하여 메모리에 저장된 변수 값 형태 그대로 파일에 저장할 수 있다.

fopen() : 이진 파일 열기

블록 단위 입출력

  • fread()와 fwrite()
    : 한번에 일정한 크기의 데이터를 파일에 읽거나 쓰기 위한 입출력 함수

  • int fread(void buf, int size, int n, FILE fp);
    : fp가 가리키는 파일에서 size 크기의 블록(연속될 바이트)을 n개 읽어서 버퍼 포인터 buf가 가리키는 곳에 저장하고 읽어온 블록의 개수를 리턴한다.

  • int fwrite(const void buf, int size, int n, FILE fp);
    : 파일 포인터 fp가 지정한 파일을 버퍼 buf에 저장되어 있는 size크기의 블록(연속된 바이트)을 n개 기록하고 성공적으로 출력한 블록 개수를 리턴한다.

  • 기본 아이디어
    : 어떤 자료형의 데이터이던지 그 데이터를 연속된 바이트로 해석해서 파일에 저장한다.

: 파일에 저장된 데이터를 연속된 바이트 형태로 읽어서 원래 변수에 순서대로 저장하여 원래 데이터를 그대로 복원

ex) rec저장


struct student rec;
FILE *fp = fopen("stfile","wb");

...
fwrite(&rec,sizeof(rec),1,fp);

ex) stcreate1.c


#include <stdio.h> 
#include "student.h"

int main(int argc, char* argv[]) 
{ 
struct student rec;
FILE *fp;
  if (argc != 2) {
  fprintf(stderr, "사용법: %s 파일이름\n",argv[0]);
  exit(1); 
  }
  
  // argv[1]을 wb-> 읽기+쓰기모드로 오픈
fp = fopen(argv[1], "wb");
printf("%-9s %-7s %-4s\n", "학번", "이름", "점수"); 

// 정보 입력받고
while (scanf("%d %s %d", &rec.id, rec.name, &rec.score) == 3) 
// rec버퍼에 기록한 내용을 파일 포인터 fp가 가리키는곳에 기록
fwrite(&rec, sizeof(rec), 1, fp);
fclose(fp);
exit(0);
}

7.4 임의접근

파일 내 위치

  • 현재 파일 위치
    : 열린 파일에서 다음 읽거나 기록할 파일 내 위치

  • 파일 위치 포인터
    : 시스템 내에 그 파일의 현재 파일 위치를 저장

  • fseek(FILE *fp, long offset, int mode)
    파일 위치 포인터를 임의로 설정할 수 있는 함수
  • rewind(FILE *fp)
    현재 파일 위치를 파일 시작에 위치시킴.
  • ftell(FILE *fp)
    • 파일의 현재 파일 위치를 나타내는 파일 위치 지정자 값 리턴

레코드 수정 과정


7.5 버퍼링

C라이브러리 버퍼 사용 목적

  1. 디스크 I/O 수행의 최소화
    -> read(),write() 함수 호출의 최소화

  2. 최적의 크기 단위로 I/O 수행

  3. 시스템 성능 향상

C라이브러리 버퍼 방식

1. 완전 버퍼 방식

  • 버퍼가 꽉 찼을때 실제 I/O 수행
  • 디스크 파일 입출력

2. 줄 버퍼 방식

  • 줄 바꿈 문자(newline)에서 실제 I/O 수행
  • 터미널 입출력(stdin, stdout)

3. 버퍼 미사용 방식

  • 버퍼를 사용하지 않는다.
  • 표준 에러(stderr)

ex)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _IO_UNBUFFERED 2
#define _IO_LINE_BUF 0x200

int main(int argc, char* argv[])
{
	FILE *fp;
 
	if(!strcmp(argv[1],"stdin")) // 표준 입력
	{
		fp = stdin;
		printf(" 한 글자 입력 : ");
		if(getchar() == EOF) perror("getchar");
	}

	else if(!strcmp(argv[1],"stdout")) // 표준 출력
	{
		fp = stdout;
	}

	else if(!strcmp(argv[1],"stderr")) // 표준 에러
	{
		fp = stderr;
	}

	else if( (fp= fopen(argv[1],"r")) == NULL)
	{
		perror("fopen");
		exit(1);
	}
	else if(getc(fp)==EOF) perror("getc");


	printf("스트림 = %s ",argv[1]);

	if(fp-> _flags & _IO_UNBUFFERED)
	{
		printf("버퍼 미사용");

	}
	else if( fp->_flags & _IO_LINE_BUF)
	{
		printf("줄 버퍼 사용");
	}
	else
	{
		printf("완전 버퍼 사용");
	}

	printf(", 버퍼 크기 = %d\n",fp->_IO_buf_end - fp->_IO_buf_base);
	exit(0);


}

버퍼 관리 방법 변경(setbuf() / setvbuf() )

#include <stdio.h>

void setbuf (FILE *fp, char *buf );
int setvbuf (FILE *fp, char *buf, int mode, size_t size );
  • 호출 시기
    : 스트림이 오픈된 후, 입출력 연산 수행 전에 호출되어야 함.

1. void setbuf (FILE fp, char buf )

: 버퍼 사용을 on/off 할 수 있다.

: buf 가 NULL이면 버퍼 미사용 방식
: buf가 BUFSIZ 크기의 공간을 가리키면 완전/줄 버퍼 방식

  • 터미널 장치면 줄버퍼 방식

  • 그렇지않으면 완전 버퍼 방식

    2. int setvbuf (FILE fp, char buf, int mode, size_t size )

    : 버퍼 사용방법을 변경

  • 성공하면 0, 실패하면 Non-zero 값 리턴

  • mode :
    _IOFBF : 완전버퍼방식
    _IOLBF : 줄버퍼 방식
    _IONBF : 버퍼 미사용 방식

  • 만약에 mode가 _IONBF 이면 buf와 size는 무시됨.

  • 만약에 mode가 _IOFBF or _IOLBF 이고
    buf 가 NULL 이면 라이브러리가 알아서 적당한 크기 할당 사용
    buf가 NULL이 아니면 buf에서 size만큼의 공간 사용

0개의 댓글

관련 채용 정보