본문 바로가기

STUDY/linux

File I/O functions in C Library

File I/O in C Library

지난 포스팅에서는 리눅스에서 제공하는 file-i/o 를 위한 system call 이었다.

 

File I/O system call in LINUX

UNIX 계열 운영체제에서 파일의 종류는 크게 두 종류이다. 하나는 regular file, 우리가 흔힌 생각하는 파일이다. 예를 들면 txt, c, cpp 등등 또 다른 하나는 I/O device를 file 로 추상화한 special file이다...

josushell.tistory.com

 

이와 비슷하게 파일 입출력 함수를 C 표준 라이브러리에서도 제공한다.

다음은 가장 자주 쓰이는 함수들이다.

 

  • fopen
  • fgetc/ fputc
  • fgets/ fputs
  • fread
  • fwrite
  • fseek
  • ftell
  • rewind
  • fflush

그렇다면 fopen() 과 open() 의 차이점은 무엇일까?

바로 파일을 다루는 단위의 차이이다.

 

open() 함수는 파일을 byte 단위로 다룬다. 이에 반해 fopen()은 파일을 스트림 단위로 다룬다.

또 open 함수는 파일 I/O 를 할때마다 kernel I/O 또한 같이 하게 된다. 하지만 fopen 함수는 사용자 메모리에서 데이터를 쌓아두다가 buffer가 찼을 경우에만 kernel I/O 를 하게 되어 시스템의 부하를 줄인다.

 

stream 또한 시스템에 미리 정의되어 있는 것들이 있다.

#include <stdio.h>

FILE *stdin

FILE *stdout

FILE *stderr

 


파일 오픈 함수

 

#include <stdio.h>

FILE *fopen(char *filename, char *type)

 

함수 명세

return FILE 타입의 포인터, 실패 시 NULL
description type으로 모드와 파일 타입을 부여하여 파일을 연다.

 

파라미터

char *filename 파일 명
char *type access mode + file type

 

세 번째 파라미터인 type은 파일 I/O type 을 의미한다.

 

access mode: r(읽기), w(쓰기), a(추가), r+(읽기 + 쓰기), w+(쓰기 + 읽기), a+(추가 + 읽기)

file type: t(text file), b(binary file)

 

이때 w+, r+ 차이는 어떤게 우선순위에 있냐는 차이이다.

즉 r+ 로 열게 된다면 읽기 기반에 쓰기도 추가이므로 데이터가 날라가지는 않는다.

하지만 w+ 로 열게 된다면 쓰기 기반에 읽기 추가이므로 데이터가 날라간다.

 

file type 은 두가지의 선택 사항이 있다.

text file: 모든 데이터가 ASCII 코드만으로 이루어진 경우

binary file: text file 이 아닌 모든 파일

 


파일 닫기 함수

 

#include <stdio.h>

int fclose(FILE *fp)

 

함수 명세

return 성공 시 0, 실패 시 EOF
description 파일을 닫는다.

 

파라미터

FILE *fp 파일 포인터

파일 읽기/ 쓰기 함수

 

C 표준 라이브러리에서 제공해주는 함수들은 입출력을 stream 단위로 한다. 이때 얼마나 읽는지 그 기준이 되는 것은 다양하다.

character, line, buffer 등등 상황에 맞게 사용할 수 있다.

 

character I/O : 한 글자씩 I/O

 

# include <stdio.h>

int getc(FILE *fp)

int fgetc(FILE *fp)

 

함수 명세

return 읽어 온 글자, 실패 또는 error의 경우 EOF(-1)
description 파일에서 한 글자를 읽어온다.

 

파라미터

FILE *fp 파일 포인터

 

이 두 함수의 차이점은 바로 매크로의 유무이다.

getc() 는 매크로인 반면

fgetc() 는 일반 함수이다.

 

 

# include <stdio.h>

int putc(int ch, FILE *fp)

int fputc(int ch, FILE *fp)

 

함수 명세

return 성공 시 ch, 실패 시 EOF
description 파일로 한 글자를 출력한다.

 

파라미터

int ch 출력 할 문자 ch
FILE *fp 파일 포인터

 


line I/O: 한 줄씩 I/O

 

#include <stdio.h>

char *fgets(char *buf, int max, FILE *fp)

 

함수 명세

return 성공 시 읽어 온 buf, 실패 또는 파일 끝에서 NULL
description 파일로부터 한 줄을 읽어서 buf에 저장한다.
이때 최대 읽을 수 있는 수는 max 이다.

 

파라미터

char *buf 출력 할 문자 ch
int max 버퍼의 최대 크기
FILE *fp 파일 포인터

 

이 함수에서 중요한 점은 개행문자까지 한 줄로 입력 받는다는 것이다.

또한 버퍼의 최대크기가 max 이지만 NULL로 끝나야 하므로 max-1 byte 만큼 저장 가능하다.

 

 

#include <stdio.h>

int fputs(char *str, FILE *fp)

 

함수 명세

return 성공 시 non-negative value, 실패 시 EOF(-1)
description 파일로 한 줄을 출력한다.

 

파라미터

char *str 출력 할 문자열 str
FILE *fp 파일 포인터

 

이 함수의 특징은 출력 시 개행문자가 붙지 않는다는 것이다.

따라서 개행문자까지 읽고, 출력시에는 추가로 개행문자를 붙이지 않는 것을 원한다면 fgets()와 fputs()를 활용 가능하다.

 


Binary I/O: buffer 단위로 I/O (binary file만 사용 가능)

 

# include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nobj, FILE *fp)

 

함수 명세

return 성공 시 읽어온 객체의 수, 실패 또는 end of file 일 때 0
description 파일에서 object를 읽어서 미리 지정한 ptr에 저장한다.

 

파라미터

void *ptr 파일에서 데이터를 읽어서 저장할 버퍼
size_t size 읽어 올 데이터의 크기
size_t nobj 읽어 올 데이터의 최대 갯수
FILE *fp 파일 포인터

 

이 함수의 특징은 ASCII 코드가 아닌 바이너리 데이터도 읽어서 저장할 수 있다는 점이다. 이때 읽어서 저장하고자 하는 구조체 혹은 데이터의 자료구조를 void pointer로 넘기게 된다.

 

 

# include <stdio.h>

size_t fwrite(void *ptr, size_t size, size_t nobj, FILE *fp)

 

함수 명세

return 성공 시 실제로 쓴 객체의 수, 실패 또는 end of file 일때 0
description 파일에 nobj 수 만큼 데이터를 쓴다.

 

파라미터

void *ptr 파일에 쓸 데이터가 저장된 버퍼
size_t size 파일에 쓸 데이터의 크기
size_t nobj 파일에 쓸 데이터의 최대 갯수
FILE *fp 파일 포인터

 

예를 들면 이 두 함수는 다음과 같이 사용 가능하다.

 

FILE *fp;

struct record temp;
struct record temp_array[10];

// 파일 읽기
fread(&temp, sizeof(record), 1, fp);
// 파일 쓰기
fwrite(temp_array, sizeof(record), 10, fp);

위치 조정 함수

 

# include <stdio.h>

# include <unistd.h>

 

int fseek(FILE *fp, long offset, int whence)

 

함수 명세

return 성공 시 0, 실패 시 nonzero
description whence 부터 offset만큼 파일 포인터의 위치를 옮긴다.

 

파라미터

FILE *fp 파일 포인터
long offset 파일 포인터를 옮길 offset (얼마나 옮길지)
int whence 어디서 부터 파일 포인터를 옮길지에 대한 기준점

 

이때 세 번째 파라미터로 들어가는 기준점은 다음과 같은 매크로를 사용 가능하다.

 

  • SEEK_SET(0) : 맨 앞
  • SEEK_CUR(1) : 현재 위치
  • SEEK_END(2) : 맨 끝

 

# incldue <stdio.h>

long ftell(FILE *fp)

 

함수 명세

return 성공 시 현재 파일 포인터의 위치, 실패 시 -1L
description 현재 파일 포인터의 위치를 반환한다.

 

파라미터

FILE *fp 파일 포인터

 

# include <stdio.h>

void rewind(FILE *fp)

 

함수 명세

return nothing
description 파일 포인터의 위치를 파일 맨 처음으로 이동시킨다.

 

파라미터

FILE *fp 파일 포인터

 


example

지난 포스팅에서 구현한 cp 함수는 linux system call을 사용하였다.

그렇다면 이번에는 c library를 사용하여 구현해보자.

 

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

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

	if (argc != 3)
		exit(1);

	fp1 = fopen(argv[1], "rt");
	if (fp1 < 0)
		exit(1);

	fp2 = fopen(argv[2], "wt");
	if (fp2 < 0)
		exit(1);

	while((ch = fgetc(fp1)) > 0)
		fputc(ch, fp2);

	fclose(fp1);
	fclose(fp2);
}

example2

파일 포인터 위치 조정 함수를 이용해서 파일의 사이즈를 구하는 함수를 만들어보자

핵심은 파일 포인터를 끝까지 이동시키면 그때의 파일 포인터가 파일 사이즈가 되는 것이다.

 

long file_size(FILE *fp)
{
	long	temp;
	long	size;

	temp = ftell(fp);
	if (fseek(fp, 0L, SEEK_END) == 0)
		size = ftell(fp);
	fseek(fp, temp, SEEK_SET);
	return size;
}

 

fseek() 함수를 맨 끝에서 0L 만큼 이동하면 파일 포인터를 파일 맨 끝으로 옮기게 되고 그때의 파일 포인터의 위치는 파일 사이즈이다.

파일 포인터를 파라미터로 받아서 이를 이동하게 되면 원래 위치를 잃어버리므로, 현재 파일 포인터의 위치를 temp에 저장해두고 마지막에 복구해준다.

'STUDY > linux' 카테고리의 다른 글

Files and Directories in LINUX  (0) 2022.03.31
SIGNAL in LINUX  (0) 2022.03.29
Process and Threads in LINUX  (0) 2022.03.28
File I/O system call in LINUX  (0) 2022.01.04
shell commands  (0) 2022.01.04