[C] 파일 입출력

JOOYEUN SEO·2023년 9월 6일

C 언어

목록 보기
13/13
post-thumbnail

1. 파일 입출력의 개념

표준입출력함수가 키보드나 모니터를 통해 자료를 입출력한다면, 파일을 처리하는 함수는 하드 디스크나 USB 메모리 같은 보조기억장치에 저장된 파일(file)로부터 자료를 입출력한다.
파일 입력 : 디스크의 특정 위치(파일)에 수록된 자료를 읽어 들여 기억공간에 저장하는 과정
파일 출력 : 기억공간에 있는 자료를 디스크의 특정 위치(파일)에 저장하는 과정

먼저, 자료를 입출력하면 스트림이라는 가상의 통로를 통해 데이터가 이동하는 과정을 거친다.

✏️ 스트림(stream)
파일, 콘솔 또는 소켓과 프로그램 사이에 존재하는 논리적인 통로이다.
표준 입출력 장치(키보드와 모니터)와 연결된 스트림은 프로그램 실행, 종료 시 자동으로 생성, 소멸된다.
하지만 파일 입출력을 위한 스트림은 프로그램을 통해서 생성과 소멸을 직접 시켜줘야 한다.

스트림의 내부에는 버퍼가 존재하여, 파일 입출력 시 사용하게 된다.

✏️ 버퍼(buffer)
기억공간과 디스크 사이에 존재하는 임시 기억공간이다.
파일을 입출력할 시 직접 수행하지 않고 이 임시 기억공간을 사용한다. 먼저 데이터의 일정량을 버퍼에 채운 뒤, 버퍼가 다 채워지면 들어있는 자료를 한꺼번에 처리하는 방식이다(물을 저장하는 물탱크와 같은 역할).
버퍼를 사용하는 이유는 디스크 드라이버의 처리 속도가 느리기 때문에 자료를 모아서 한번에 처리하는 편이 더 빠르게 실행할 수 있어서이다.

2. 파일 포인터를 이용한 파일 입출력

(1) 파일 입출력 순서

파일로부터 자료를 입출력하는 것을 파일 입출력이라고 한다. 파일 입출력 시 파일 포인터가 스트림(파일과 프로그램의 통로 역할)이 된다.

  1. FILE \*fp; : 변수 fp를 파일을 가리키는 포인터로 선언(실제 파일과 프로그램을 연결하는 통로)
  2. fp = fopen("파일명", "모드"); : 파일을 지정한 모드로 열고(스트림 생성), fp가 파일을 가리키게 함
  3. 입출력 수행 : 각종 파일 함수와 파일 포인터 fp를 이용하여 입출력 수행
  4. fclose(fp) : 버퍼를 비우고 fp가 가리키는 파일을 닫음(스트림 소멸)

(2) 파일처리 함수

파일처리 함수들의 원형은 헤더 파일 <stdio.h>에 선언되어 있다.

함수기능
fopen("파일명", "모드")파일을 지정한 모드로 열기
fclose()파일을 닫기
fscanf(파일 포인터명)정해진 형식에 따라 파일로부터 읽기
fprintf()정해진 형식에 따라 파일에 쓰기
feof()파일의 끝인가를 판별
ferror()파일 입출력 시 에러 발생 유무 조사

3. 파일 포인터 선언과 파일 여닫기

(1) 실행 과정

📄 input.txt

15

📄 output.txt

예제 코드와 같은 디렉터리 안에 텍스트 파일 2개를 생성한다. 파일 input.txt 에는 임의의 숫자를 입력한 뒤 저장하고, 파일 output.txt 은 빈 상태로 둔다.

⌨️ 예제 1

#include <stdio.h>

int main() {
    FILE *in, *out;   			// 파일 포인터 in, out선언
    int n;

    // 파일 input.txt을 읽기 전용으로 열어서, 그 시작 주소를 in에 건네주는 fopen문
    in = fopen("input.txt", "r");
    // 파일 output.txt를 쓰기 전용으로 열어서, 그 시작 주소를 out에 건네주는 fopen문
    out = fopen("output.txt", "w");
    fscanf(in, "%d", &n);       // in이 가리키는 파일의 내용을 지정된 형식대로 읽어들임
    fprintf(out, "%d\n", n);    // 읽은 자료를 out이 가리키는 파일에 지정된 형식대로 출력함

    fclose(in);                 // 파일 input.txt을 닫음
    fclose(out);                // 파일 output.txt을 닫음
}

🖥️ 실행 결과
📄 output.txt

15

코드를 실행하면 파일 input.txt 에 있던 내용이 파일 output.txt 에 그대로 출력된다.

한편, C 언어는 main 함수가 끝나면 자동으로 파일을 닫기 때문에 위 예제에서 fclose 함수를 생략해도 정상 작동한다. 하지만 동시에 많은 파일을 열면 에러가 날 수 있기 때문에, 파일 하나를 열었으면 해당 작업이 끝난 후 fclose로 바로 닫는 것이 좋다.

(2) fopen 함수

파일을 열 때 사용하는 함수로, 지정된 파일을 가리키는 파일 포인터의 주소값을 반환한다.

파일명 : 파일명은 확장자까지 적어야 하며, 드라이브나 경로를 포함할 수 있다.

사용 모드 : 개방한 파일의 사용 용도를 결정한다.

사용 모드기능파일이 있을 경우파일이 없을 경우
"r"파일 읽기정상 처리NULL 값 반환
"r+"파일 읽기, 쓰기, 추가정상 처리NULL 값 반환
"w"파일 쓰기, 추가이전 내용 덮어씀새 파일 생성
"w+"파일 읽기, 쓰기, 추가이전 내용 삭제새 파일 생성
"a"파일 추가이전 내용 뒤에 추가새 파일 생성
"a+"파일 읽기, 추가이전 내용 뒤에 추가새 파일 생성

⌨️ 예제 2

// 존재하지 않는 파일을 "r" 모드로 열 경우
#include <stdio.h>

int main() {
    FILE *in;
    int n;

    in = fopen("intput.txt", "r");  // 올바른 파일명은 input.txt 이어야 함

    printf("in = %p\n", in);        // in이 가리키는 주소값을 확인해 보기

    fscanf(in, "%d", &n);
    printf("%d\n", n);
    fclose(in);
}

🖥️ 실행 결과
에러 발생

in = 0x0

출력된 in이 가리키는 주소값은 10진수로 변환하면 0, 즉 NULL을 의미한다(가리키는 것이 없음).
in이 아무것도 가리키고 있지 않는데도 읽기 모드로 파일을 개방하려 했기 때문에 에러가 발생한 것이다.
사용자가 파일명을 잘못 쓰거나, 실수로 파일을 지워버리는 등의 실수를 할 경우 이런 결과가 나올 수 있다.

🛠️ 코드 수정

// if문으로 오류 방지
#include <stdio.h>

int main() {
    FILE *in;
    int n;

    in = fopen("intput.txt", "r");

    if(in != NULL) {
        fscanf(in, "%d", &n);
        printf("%d\n", n);
        fclose(in);
    }
}

🖥️ 실행 결과
in이 아무 파일도 가리키지 않아서 NULL 값이 들어갔기 때문에 if문을 실행하지 않고 프로그램이 종료됐다.


(3) EOF(End of File)

EOF는 파일의 끝을 나타내는 신호로, -1의 값을 갖고 있다.

⌨️ 예제 3
ex1.c

// 자기 자신을 출력하는 프로그램
#include <stdio.h>

int main() {
    FILE *in = fopen("ex1.c", "r");     // 파일 ex1.c 자체를 열게 됨
    char ch;
    while(!feof(in)) {                  // feof 함수가 참이 되는 순간, 거짓이 돼서 루프 종료
        fscanf(in, "%c", &ch);          // 파일 ex1.c의 내용을 한 글자씩 ch에 입력받음
        printf("%c", ch);               // 입력받은 내용 출력
    }
    fclose(in);
}

🖥️ 실행 결과

// 자기 자신을 출력하는 프로그램
#include <stdio.h>

int main() {
    FILE *in = fopen("ex1.c", "r");     // 파일 ex1.c 자체를 열게 됨
    char ch;
    while(!feof(in)) {                  // feof 함수가 참이 되는 순간 !로 거짓이 돼서 루프 종료
        fscanf(in, "%c", &ch);          // 파일 ex1.c의 내용을 한 글자씩 ch에 입력받음
        printf("%c", ch);               // 입력받은 내용 출력
    }
    fclose(in);
}}

feof 함수는 End of File의 약자를 따온 것으로, 파일을 끝까지 읽었을 때 true를 반환한다.

예제 코드가 그대로 출력됐지만, 맨 마지막에 }가 하나 더 출력되어 버렸다. 그 이유는 맨 마지막 문자까지 출력됐어도 아직 feof 함수의 값은 거짓이어서 루프 한 바퀴를 더 돌기 때문이다. 마지막 }의 다음에 오는 문자가 없기 때문에 한 번 더 }를 입력받게 된다.

🛠️ 코드 수정

// 자기 자신을 출력하는 프로그램
#include <stdio.h>

int main() {
    FILE *in = fopen("ex1.c", "r");
    char ch;
    while(fscanf(in, "%c", &ch) != EOF) { 
        printf("%c", ch);
    }
    fclose(in);
}

🖥️ 실행 결과

// 자기 자신을 출력하는 프로그램
#include <stdio.h>

int main() {
    FILE *in = fopen("ex1.c", "r");
    char ch;
    while(fscanf(in, "%c", &ch) != EOF) { 
        printf("%c", ch);
    }
    fclose(in);
}

fscanf 함수 자체를 while문 조건으로 넣었다. fscanf 함수는 파일 끝에 도달하면 EOF를 반환하는 성질이 있기 때문이다.

EOF가 가진 값은 -1이기 때문에, fscanf가 읽어들인 값이 -1이 아닐 때까지 루프를 계속 반복하게 된다. 이 코드에서는 파일의 끝에 도달하는 경우 while문 안에 들어가지 않고 바로 끝나기 때문에 마지막 문자가 중복 출력되는 것을 막을 수 있다.




▷ 김형근·곽덕훈·정재화, 『C프로그래밍』, 한국방송통신대학교출판문화원, 2020
▷ 서현우, 『뇌를 자극하는 C 프로그래밍』, 한빛미디어, 2005
▷ 두들낙서, “두들낙서의 C/C++ 강좌”, https://youtube.com/playlist?list=PLlJhQXcLQBJqywc5dweQ75GBRubzPxhAk&si=s8RNpRcpJTi_Lz8M

0개의 댓글