티스토리 뷰

창병모, 리눅스 프로그래밍 원리와 실제(생능출판, 2022)

4.1 파일 및 파일 포인터

- 파일을 사용하기 위해서 응용 프로그램은 위 그림처럼 시스템 호출을 통해 필요할 때마다 커널에 서비스 요청 가능

- 하지만 파일을 보다 쉽게 사용하는 방법은 C언어가 제공하는 표준 라이브러리 함수를 사용하는 것

 

파일

- C 파일은 위 그림처럼 모든 데이터를 연속된 바이트 형태로 저장하는데 저장된 데이터에 따라 텍스트 파일과 이진 파일로 구분할 수 있음

 

텍스트 파일(text file)

- 문자들만으로 이루어진 파일

- 한글, 영문, 숫자 등의 문자들을 포함

- 여러 개의 줄로 이루어지며 매 줄마다 \n을 포함

 

이진 파일(binary file)

- 모든 데이터를 컴퓨터 내부의 이진수 표현 그대로 저장한 파일

- 이미지 파일이나 실행 파일 

- 이진 파일을 이용하여 메모리에 저장된 변수 값을 이진수 표현 그대로 파일에 저장 가능

 

C 언어 파일 입출력

1. 파일 열기 : fopen() 함수 사용

2. 파일 입출력 : 다양한 파일 입출력 함수 사용

3. 파일 닫기 : fclose() 함수 사용

 

파일 열기와 FILE 포인터

- C 파일에서 파일을 사용하기 위해서는 반드시 fopen()을 사용해 파일열기를 해야 함

- 파일을 열지 않고서는 파일 접근 불가

- 또한 파일을 이용한 이후에는 fclose()를 이용해 파일 닫기를 해야 함

 

- fopen()을 하면 위 그림과 같이 파일에 대한 정보가 FILE 구조체 형태로 저장되고 이 FILE 구조체에 대한 포인터가 반환

- FILE 포인터(FILE pointer)

* FILE 포인터는 열린 파일을 가리키는 포인터

 

- 파일 입출력 함수들은 보통 입출력 최적화를 위해서 버퍼를 사용하는데 이 버퍼는 입출력의 중간에서 입출력하는 데이터를 모아두었다가 일정한 크기가 되면 한꺼번에 보내주는 역할을 함

- FILE 구조체는 버퍼에 대한 정보와 파일 접근 모드, 열린 파일의 식별 번호를 나타내는 파일 디스크립터 등을 포함

 

파일 열기 함수 fopen()

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

- filename이 나타내는 파일에 대해 파일 입출력 모드 mode로 파일을 염

- 성공하면 열린 파일을 나타내는 FILE 포인터를, 실패하면 NULL 반환

 

읽기 전용으로 열기

fp = fopen("/text.txt", "r");

 

쓰기 전용으로 열기

fp = fopen("out.txt", "w");

 

추가 모드로 열기

- 추가(append) 모드로 파일을 열 때는 파일이 이미 존재하면 기존 파일에 끝에 붙여서 씀

fp = fopen("out.txt", "a");

 

수정 모드로 열기

- "r+", "w+", "a+"는 기본적으로 수정(읽기와 쓰기)을 위한 입출력 모드로 대상 파일이 있을 때의 동작은 r, w, a 모드와 같음

- 대상 파일이 존재하지 않으면 NULL 반환

 

스트림

- fopen()에 의해 파일이 열리면 이를 스트림(stream)이라고 함

- 스트림 : 버퍼형 파일 입출력을 위한 논리적 인터페이스

 

파일 닫기

fclose(FILE *fp);

- fp가 가리키는 파일을 닫음

- 성공하면 0, 오류면 -1 반환

 

 

4.2 텍스트 파일

문자 단위 입출력

fgetc(FILE *fp)

- fp가 가리키는 파일에서 한 문자를 읽어서 반환

- 파일 끝에 도달했을 경우에는 EOF(-1) 반환

 

fputc(int c, FILE *fp)

- fp가 가리키는 파일에 한 문자씩 출력하고 출력된 문자 반환

- 출력 시 오류가 발생하면 EOF(-1) 반환

- 명령줄 인수의 개수를 확인해 있다면 파일 열기

- c에 getc로 읽은 문자 저장

- while문으로 파일 끝 아닐 때까지 읽은 문자 표준출력에 출력

 

파일 복사 예제

- 명령줄 인수로 파일 두개를 받아 fp1, fp2에 각각 넣고 읽기 전용, 쓰기 전용으로 열기

- 파일이 열리면 fgetc(fp) 함수를 호출해 첫번째 파일로부터 한 문자씩 읽어서 읽은 문자를 fputc(c, fp2)를 이용해 두번째 파일에 쓰기

- fp1을 fp2에 복사

 

feop(FILE *fp)

- fp가 가리키는 파일의 끝에 도달하면 0이 아닌 값을 반환하고 파일 끝이면 0 반환

ungetc(int c, FILE *p)

- c에 저장된 문자를 입력 스트림에 반납, 마치 문자를 읽지 않은 것처럼 파일 위치 포인터 1 감소

 

줄 단위 입출력

char* fgetc(char *s, int n, FILE *fp)

- fp가 가리키는 파일로부터 한 줄을 읽어서 문자열 포인터 s에 저장하고 s 반환

fputs(const char *s, FILE *fp)

- 문자열 s를 fp가 가리키는 파일에 출력

- 성공하면 출력한 바이트 수, 실패하면 EOF 반환

 

파일 번호와 줄번호 출력 예제

- 명령줄 인수로 받은 파일을 읽기 전용으로 열기

- while문에서 fgetc 함수를 이용해 한줄씪 읽음

- 읽을 때마다 줄 수를 증가시키고 프린트

 

포맷 입출력

- fprintf() 함수를 이용해 파일에 데이터를 지정한 포맷대로 출력 가능

- printf() 함수는 표준출력(stdout)에 출력하는 반면 fprintf() 함수는 파일에 출력 <- 거의 같은 기능

 

fprintf(FILE *fp, const char *format, ...)

- fp는 출력할 파일을 가리키는 FILE 포인터이고 두 번째부터의 인자는 printf 함수와 동일

 

fscanf(FILE *fp, const char *format, ...)

- fp는 입력받을 파일을 가리키는 FILE 포인터이고 두 번째부터의 인자는 scanf 함수와 동일

- 읽은 개수 반환

 

 

4.3 이진 파일

이진 파일과 블록 단위 입출력

- 이진 파일은 모든 데이터를 컴퓨터 내부의 이진수 표현 그대로 저장한 파일로 이미지 파일이나 실행 파일 등이 이진 파일

- 이진 파일을 이용해 메모리에 저장된 변수 값을 이진수 표현 그대로 파일에 저장할 수 있음

- 이진 파일에는 한꺼번에 일정한 크기의 연속된 데이터(블록)를 읽거나 쓸 수 있음

- fread(), fwrite() 함수는 한 번에 일정한 크기의 연속된 데이터(블록)를 이진 파일에서 읽거나 이진 파일에 쓰기 위한 함수

- 두 함수는 size 크기의 블록을 n개 읽거나 쓰며 실제 쓴 블록의 개수 반환

- fread() 함수를 사용할 때 주의할 점은 buf가 가리키는 버퍼의 크기가 읽어 올 데이터의 크기와 같거나 커야 한다는 점

 

fread(void *buf, int size, int n, FILE *fp)

- fp가 가리키는 파일에서 size 크기의 블록을 n개 읽어서 포인터 buf가 가리키는 곳에 저장

- 읽어온 블록의 개수 반환

 

fwrite(const void *buf, int size, int n, FILE *fp)

- 파일 포인터 fp가 가리키는 파일에 buf에 저장되어 있는 size 크기의 블록을 n개 기록

- 성공적으로 출력한 블록 개수 반환

 

- fwrite() 함수는 첫 번째 매개변수로 연속된 바이트에 대한 시작주소(포인터)를 받음

- 어떤 자료형의 데이터이던지 그 데이터를 연속된 바이트로 해석해 파일에 저장

- 이진 파일에 저장된 데이터로부터 원래 데이터를 복원하기 위해서는 fread() 함수를 이용해 파일에 저장된 데이터를 연속된 바이트 형태로 읽어서 원래 자료형 변수에 순서대로 저장하면 가능!

 

4.4 임의 접근

사용자가 원하는 자료가 있는 곳을 직접 임의 접근(random access)할 수 있는 방법

 

임의 접근 fseek()

- 열린 파일에서 다음 읽거나 쓸 파일 위치를 현재 파일 위치라고 하며 시스템 내에 파일 위치 포인터가 가리키고 있음

fseek(FILE *fp, long offset, int mode);

- fp가 가리키는 파일의 현재 파일 위치를 기준점 mode을 기준으로 offset만큼 이동시킴

 

rewind(FILE *fp);

- fp가 가리키는 파일의 현재 파일 위치를 파일 시작점으로 이동시킴

 

ftell(FILE *fp)

- fp가 가리키는 파일의 현재 파일 위치 반환

 

 

 

* 레코드를 파일 끝에 관계없이 계산된 위치에 저장하는데 매우 유용

 

임의 접근을 이용한 레코드 수정

(1) 파일로부터 해당 레코드를 읽어서

(2) 이 레코드를 수정한 후에

(3) 수정된 레코드를 다시 파일 내의 원래 위치에 써야 함

 

4.5 버퍼 입출력

- C 표준 입출력 라이브러리에서는 입출력 최적화를 위해 버퍼를 할당해 사용

- 그 목적은 실제 디스크에 입출력하는 read(), write()와 같은 시스템 호출 횟수를 최소화함으로써 시간이 오래 걸리는 디스크 입출력을 최소화하여 입출력 시스템 성능을 향상시키는 것

- fputc() 호출을 할 때마다 직접 파일에 쓰지 않고 그 내용을 버퍼에 저장

- 위 과정을 반복해 버퍼가 꽉 차게 되면 그때 한 번에 write() 시스템 호출을 해 버퍼에 저장된 내용을 모두 디스크에 저장

-> 디스크 입출력 최소화

 

버퍼 방식

완전 버퍼(fully buffered) 방식

- 버퍼를 사용해 디스크 입출력 수행 <- 파일 입출력에 주로 사용

- 시스템 성능 향상을 위해 가능하면 실제 디스크 입출력 횟수를 최소화 해야 함

- 출력의 경우 보통 버퍼가 꽉 찰 때까지는 출력 내용을 버퍼에 저장하고 버퍼가 꽉 찼을 때 write()를 사용해 한 번에 디스크에 씀

- 버퍼의 크기는 한 블록으로 설정

 

줄 버퍼(line buffered) 방식

- 새줄문자('\n')를 만나면 실제 입출력을 수행하는 방식으로 터미널을 통한 표준 입출력에서 주로 사용

- 엔터키를 치면 한 줄의 끝으로 인식해 그 때 실제 입력이 이루어짐

 

버퍼 미사용(unbuffered) 방식

- 입출력에서 버퍼를 사용하지 않는 방식으로 표준오류에서 주로 사용

 

버퍼 설정 함수 setbuf()

setbuf(FILE *fp, char *buf)

- fp가 나타내는 파일(스트림)의 버퍼 사용을 on/off 함

 

버퍼 설정 함수 setvbuf()

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

- fp가 나타내는 파일(스트림)의 버퍼 사용 방법 변경

- 성공하면 0을 반환하고, 실패하면 0이 아닌 값을 반환

- _IOFBF : 완전 버퍼

- _IOLBF : 줄 버퍼

- IONBF : 버퍼 미사용

 

버퍼 비우기

fflush(FILE *fp)

- fp가 나타내는 스트림의 버퍼를 비움

- 성공하면 0, 실패하면 EOF(-1)를 반환

 

 

Comments