C언어) 입출력 이야기 3

Lapis0875·2022년 12월 12일
0

c언어

목록 보기
19/21
post-thumbnail

🖥️ 입출력 이야기 (3)

표준 입출력과 문자열에서 입출력을 해 봤으니, 마지막으로 파일을 다뤄볼게요. 파일 입출력은 파일을 열어, 읽거나 쓰고, 파일을 닫는 순으로 진행하니, 순서대로 알아볼게요.

파일을 여는 fopen

파일 입출력을 위해서는, 파일을 열어야 해요. 파일을 열지 않고는 파일의 내용을 볼 수도 없고, 파일에 내용을 쓸 수도 없어요.

fopen 함수는 파일 이름과 모드를 인자로 받아요.

FILE * fopen(const char * restrict filename, const char * restrict modes);

fopen()은 지정한 모드로 파일을 연 뒤 파일에 접근하기 위한 FILE 구조체의 포인터를 반환해요. 만약 파일을 열지 못할 경우, NULL을 반환해요.

파일 모드

파일의 모드는 아래와 같아요.

모드설명
"r"읽기 위해 텍스트 파일 열기
"w"쓰기 위해 텍스트 파일 열기 (기존 내용을 지워요)
"a"첨부하기 위해 텍스트 파일 열기 (기존 내용을 유지하고, 뒤에 이어서 써요)
"rb"읽기 위해 이진 파일 열기
"wb"쓰기 위해 이진 파일 열기
"ab"첨부하기 위해 이진 파일 열기

모드 뒤에 +를 붙이면, 파일을 읽기와 쓰기 모두 가능하게 열어요.

사용 예

FILE *fp = fopen("input", "r");
if (fp == NULL)
{
	printf("파일을 열 수 없습니다!");
    return -1;
}
// 파일 읽기

표준 파일 포인터

fopen이 반환하는 포인터는 파일 포인터에요. 하지만, stdio.h에 기본적으로 정의된 표준 파일 포인터가 존재해요.

파일 포인터종류설명
stdin표준 입력 파일키보드
stdout표준 출력 파일콘솔 화면
stderr표준 에러 파일콘솔 화면

파일에서 한 문자씩 읽고 쓰기

이전에, 표준 입력에서 한 문자씩 읽는 getchar 함수와 표준 출력에서 한 문자씩 쓰는 putchar 함수에 대해 배웠어요. 파일에 대해 동일한 기능을 하는 함수가 존재해요.

int getc(FILE * stream);
int putc(int c, FILE * stream);

getc

FILE *fp = fopen("file", "r");
// ...
char c = getc(fp);

putc

FILE *fp = fopen("file", "w");
//	...
getc('a', fp);

표준 입출력 파일을 사용하면?

표준 입출력 파일을 사용하면, getc(stdin)getchar() 함수와 동일하게 사용할 수 있고
putc(c, stdout)putchar(c) 함수와 동일하게 사용할 수 있어요.

파일 버전 입출력 함수

이전에 문자열을 사용하는 printf와 scanf를 다뤄봤어요. 역시나 파일에서 값을 읽는 fscanf와, 파일에 값을 쓰는 fprintf 함수도 존재해요.

int fscanf(FILE * restrict stream, const char * restrict format, ...);
int fprintf(FILE * restrict stream, const char * restrict format, ...);

첫 번째 인자는 파일 포인터를 받고, 이후의 인자는 기존의 printf, scanf와 동일해요.

활용 예

간단하게, file2.in이라는 파일에서 이름과 나이를 읽어 file2.out 파일에 "{이름}은(는) {나이}살 입니다." 라고 쓰는 코드를 작성해볼게요.

file2.in

name: 홍길동, age: 20

file2.c

#include <stdio.h>

int main(void)
{
FILE in, out;
char name[10] = {};
int age;

if ( ( in = fopen("file2.in", "r") ) == NULL)
{
    fprintf(stderr, "file2.in 파일을 열 수 없습니다!\n");
    return -1;
}
if ( ( out = fopen("file2.out", "w") ) == NULL)
{
    fprintf(stderr, "file2.out 파일을 열 수 없습니다!\n");
    return -1;
}

fscanf(in, "name: %[^,\n\t], age: %d\n", name, &age);
fprintf(out, "%s은(는) %d살 입니다.", name, age);
fclose(in);
fclose(out);
return 0;

}


> file2.in 파일이 없을 경우
```sh
file2.in 파일을 열 수 없습니다!

코드 실행 후, file2.out

홍길동은(는) 20살 입니다.

파일을 닫는 fclose

파일로 할 일을 마친 뒤에는, 반드시 fclose()를 사용해 파일을 닫아줘야 해요. fclose()는 fopen에서 반환된 FILE의 포인터를 인자로 받아요.
앞선 예제에서, main함수의 하단에 fclose를 사용했어요.

fclose(in);
fclose(out);

💡 파일 포인터도, 동적할당한 메모리를 free 해주듯이 항상 fclose로 닫아주세요!

파일의 임의 위치에 접근하기

파일 입출력에 관련된 함수들은 이전에 입출력을 마친 곳부터 이어서 입출력을 진행해요. 이는 FILE 포인터가 파일 위치 지시자를 통해, 어디까지 읽었는지를 기억하기 때문이에요.
파일 위치 지시자와 관련된 함수들은 아래와 같아요.

  • ftell()
  • fseek()
  • rewind()

어디까지 읽었는지 알려줘, ftell!

ftell 함수는 파일 위치 지시자의 현재 값을 반환해요.

long ftell(FILE * stream);

앞서 fprintf, fscanf의 예제 코드에서, fclose 전에 아래 두 문장을 추가한 후, 코드를 실행해 결과를 확인해볼게요.

	// ...
    printf("in의 위치 지시자 : %ld\n", ftell(in));
    printf("out의 위치 지시자 : %ld\n", ftell(out));
    // fclose...

출력 값을 확인해보면, long 형의 값으로 현재 파일을 어디까지 읽었는지 확인해볼 수 있어요.

in의 위치 지시자 : 24
out의 위치 지시자 : 34

여기부터 읽어줘, fseek!

fseek는 파일 위치 지시자의 값을 직접 지정해요.

ftell과 fseek를 활용해, 파일의 내용을 역순으로 출력하는 코드를 작성해볼게요.

#include <stdio.h>

int main(void)
{
    FILE *in;
    char c;

    if ( ( in = fopen("file3.in", "r") ) == NULL)
    {
        fprintf(stderr, "file3.in 파일을 열 수 없습니다!\n");
        return -1;
    }

    fseek(in, 0, SEEK_END);
    long length = ftell(in);
    printf("> 파일 길이 : %ld\n", length);
    if (ftell(in) == 0)
        return 0;   // 파일에 내용이 없습니다.
    fseek(in, -1, SEEK_CUR);

    for (int i = 0; i < length; i++)
    {
        c = getc(in);
        putchar(c);
        if (ftell(in) == 0)
            break;
        // 한번 getc()로 읽으면, 위치 지시자가 1만큼 이동한다.
        // 역순으로 이동하기 위해서는 -2 필요.
        fseek(in, -2, SEEK_CUR);
    }
    putchar('\n');
    fclose(in);
    return 0;
}

다시 처음으로, rewind

이세돌 RE:WIND 썸네일

자료출처 : 유튜브 영상 썸네일 https://www.youtube.com/watch?v=mccbePhAFsI

아 이게 아니라고요?

rewind는 이름처럼, 파일의 위치 지시자를 파일의 제일 앞으로 되돌려요.

void rewind(FILE * stream);

fseek(FILE*, 0, SEEK_SET) 과 같은 기능을 해요.

profile
새내기 대학생 개발자에요 :D

0개의 댓글