Computer Science/C

C언어 11강 | File I/O | 끝 부분 아직 하는 중

토마토. 2021. 12. 17. 14:58

file I/O

이번 강의를 요약해보자.

#include <stdio.h>에 있는 파일들을 이용하는데, 

FILE *variable_name이라는 file 포인터가 있다. 

이 구조체는 파일의 상태에 대하여 서술하는 것임

fopen(), fprintf(), fscanf(), fputc(),fgetc(), fclose() 함수로 파일을 조작한다. 


fopen(), fclose()

함수부터 시작해보자. 

기본적인 골격은 다음과 같다.

step 1. 파일 포인터 선언

FILE *fp;

step 2. 파일 오픈

fp = fopen("filename", "mode");

step 3. 어쩌구저쩌구 진행

step 4. 파일 닫기

fclose(fp);

 

이때 fopen의 mode에는 다양한 것이 있다. 

[1] text file용

fp = fopen("filename", "r"); // for reading - 주로 쓰는 것 1

fp = fopen("filename", "w"); // for writing - 주로 쓰는 것 2

fp = fopen("filename", "a"); // for appending

[2] binary file용

fp = fopen("filename", "rb"); // for reading

fp = fopen("filename", "wb"); // for writing

fp = fopen("filename", "ab"); // for appending

 

 

formatted file I/O

stdio.h 헤더 파일에서 가져다가 쓸 수 있는 파일 입출력 관련 함수에는

대표적으로 fopen, fclose, fscanf, fprintf가 있다. 

fscanf의 프로토타입은 : fscanf(ifp, control_string, other_arguments);

fprintf의 경우는 : fprintf(ofp, control_string, other_arguments);

#include <stdio.h>

int main(void){
    FILE *ifp, *ofp;
    ifp = fopen("in_file.txt", "r");
    ofp = fopen("out_file.txt", "w");
    int i;

    fscanf(ifp, "%d", &i);
    fprintf(ofp, "%d", i);

    fclose(ifp);
    fclose(ofp);

    return 0;
}

ex)

#include <stdio.h>
void main(void){
    FILE *ifp, *ofp;
    int a, sum=0;

    ifp = fopen("infile", "r");
    ofp=fopen("outfile", "w");

    while (fscanf(ifp, "%d", &a)==1){
        sum += a;
    }
    fprintf(ofp, "The sum is %d.\n", sum);

    fclose(ifp);
    fclose(ofp);
}

 

 

 

stdin, stdout, stderr

stdin : standard input file - keyboard

stdout : standard output file - screen

stderr : standard error file - screen

 

fprintf(stdout, );는 printf();

fscanf(stdin, );는 scanf();와 같다. 

(원래 ifp, ofp를 stdout, stdin으로 바꾼 것)

 

character file I/O

[1] read

파일로부터 character를 받아와보자!

EOF를 return하기 전까지 반복하면 된다. 

c = getc(fp);

c = fgetc(fp);

[2] write

에러 발생시 EOF를 return함. 

putc(c, fp);

fputc(c, fp);

 

EOF == End of File

stdio.h에서는 EOF를 #define EOF -1로 정의하고 있음. 

EOF인지 확인하려면..int feof(FILR *fp); 이용할 수 있다. 

 

file을 쭉 받아오려면 어떻게 해야하지? 

 

ex) copy file1 file2

#include <stdio.h>
int main(void){
    FILE *fp1, *fp2;
    int c;

    fp1 = fopen("in.txt", "r");
    fp2 = fopen("out.txt", "w");
    while ((c=getc(fp1))!=EOF){
        putc(c,fp2);
    }
    fclose(fp1);
    fclose(fp2);
    return 0;
}

파일명을 컴파일 단계에서 말하도록 할 수도 있다. 

argc, *argv[] 싫어엉

#include <stdio.h>
int main(int argc, char *argv[]){
    FILE *fp1, *fp2;
    int c;
    fp1 = fopen(argv[1], "r");
    fp2 = fopen(argv[2], "w");
    while ((c=getc(fp1))!= EOF)
        putc(c,fp2);
    fclose(fp1);
    fclose(fp2);
    return 0;
}

 

error handling and exit

 

 

ex) double spacing a file

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

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

    if (argc != 3) exit(1);
    fp1 = fopen(argv[1], "r");
    fp2 = fopen(argv[2], "w");
    while ((c=getc(fp1))!=EOF) {
        putc(c, fp2);
        if (c=='\n') putc('\n', fp2);
    }
    fclose(fp1);
    fclose(fp2);
    exit(0);
}

 

line I/O

line 중심 인풋 함수로 fgets가 있고, 

아웃풋 함수로 fputs가 있다. 

두 함수의 용례를 살펴보자. 

 

- fgets() -

line oriented input function

char *fgets(char *line, int n, FILE *fp);

최대 n-1개의 character를 한 줄에서(*line) 읽어들인다. 

만약 다음 줄이 오거나, 파일이 끝나면 함수가 종료됨

버퍼용으로 배열 끝에(*line) '\0'이 삽입된다. 

char *fgets(char *s, int n, FILE *iop){
	
    int c;
    char *cs;
    
    cs = s;
    while ((c=getc(iop))!=EOP &&--n>0)
    	if ((*cs++ = c) == '\n') break;
	*cs = '\0';
    
    return (c==EOF && cs==s)?NULL:s;
}

 

- fputs() -

line oriented output function

int fputs(char *line, FILE *fp);

null로 끝나는 string line을 카피해서 fp로 보낸다. 

성공하면 nonnegative value를 return하고, 실패하면 EOF(-1) return함

 

함수 내부 구조는

int fputs(char *s, FILE *iop)
{
	int c;
    while (c=*s++) putc(c, iop);
    return ferror(iop) ? EOF : 0;
}

 

-fputs(), fgets() 용례 -

매우 중요

#include <stdio.h>

int main(void){
    FILE *ifp = fopen("input.txt", "r");
    FILE *ofp = fopen("output.txt", "w");

    if (ifp == NULL){
        printf("ERROR\n");
        return 0;
    }
    while (1){
        char arr[100];
        char *tmp = fgets(arr, 100, ifp);
        if (tmp == NULL){
            break;
        }
        fputs(arr, ofp);
    }

    fclose(ifp);
    return 0;
}
#include<stdio.h>
int main(void)
{
    //read mode로 파일을 생성하고 오픈합니다.
    FILE* pFile = fopen("test.txt", "r");
    if(pFile == NULL)
    {
        //파일이 없다. 프로그램 종료
        return 0;
    }
 
    //파일의 끝이 나올때 까지 반복문으로 fgets 수행.
    while(true)
    {
        char arr[100];//문자열을 담을 변수
        char* pStr = fgets(arr, 100, pFile);
        if(pStr == NULL)
        {
            //파일의 끝
            break;
        }
 
        //파일 계속 읽어들이기. (출력에 \n가 없음.)
        printf("fgets 1sr arg : %s", arr);
    }
    
    //파일 닫기
    fclose(pFile);
    return 0;
}

[C언어/C++] fgets, fputs 함수에 대해서 (tistory.com)


random File I/O

파일 데이터를 읽는 방식에는 순차 엑세스와 랜덤 엑세스가 있다.

랜덤 파일 엑세스는 임의의 위치로 이동해서 필요한 부분만 읽고 쓰는 방식을 말한다. 

C에서는 fseek, ftell로 이를 수행한다. 

 

프로토타입 형식

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

long ftell(FILE *fp);

 

fseek() 함수

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

fp : 탐색하는 파일의 FILE형 변수 포인터

offset : place 위치에서 이동하는 바이트 수

place : stdio.h 헤더 상수 중 하나(SEEK_CUR / 현재 파일 포인터의 위치 , SEEK_SET / 파일의 시작, SEEK_END / 파일 끝)

 

ftell() 함수

long ftell(FILE *stream);

지정된 스트림의 함수 포인터를 돌려줌

 

ex)

#include <stdio.h>
int main(void){
    int c;
    FILE *ifp;
    ifp = fopen("in.txt", "r");
    fseek(ifp, 0, SEEK_END);
    fseek(ifp, -1, SEEK_CUR);
    while (ftell(ifp)>0){
        c = getc(ifp);
        putchar(c);
        fseek(ifp, -2, SEEK_CUR);
    }
    fclose(ifp);
    return 0;
}

cf)

#include <stdio.h>

int main(void){
    char fileName[256];
    int fileIndex, text;
    FILE *file;

    printf("읽을 파일 이름을 지정하십시오.");
    scanf("%s", fileName);
    printf("파일을 읽을 시작 위치를 바이트 단위로 지정하십시오.");
    scanf("%d", &fileIndex);

    file = fopen(fileName, "rb");
    if (file == NULL){
        fprintf(stderr, "파일 조작에 에러가 발생했습니다.\n");
        return 0;
    }
    
    fseek(file, fileIndex, SEEK_SET);
    printf("파일 위치 %d에서 읽습니다\n", (int)ftell(file));

    while (1){
        text = fgetc(file);
        if (feof(file)) break;
        printf("%c", text);
    }

    printf("\n");
    fclose(file);
    return 0;
}

 

low level I/O

system call : 운영체제 커널에 보내는 리퀘스트

create() : 새로운 빈 파일을 만들거라

int create(char *filename, mode_t, mode)

 

open() : 파일을 오픈해라

int open(const char* Path, int flags [, int mode ]);

 

close() : file 닫아라

int close(int fd);

 

read() : file을 읽어와라

size_t read(int fd, void * buf, size_t cnt);

 

write() : 파일을 써라

size_t write(int fd, void *buf, size_t cnt);

 

open creat close

??

 

file access permissions

read, write

ex) read(), write()

#include <unistd.h>
#include <stdio.h>
int getchar(void);

int main(void){
    char buf[BUFSIZ];
    int n;
    while ((n=read(0,buf,BUFSIZ))>0)
        write(1,buf,n);
    return 0;
}

int getchar(void){
    char c;
    return (read(0,&c,1)==1)?(unsigned char)c:EOF;
}

 

ex) open, creat, close

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

#define PERMS 0666

int main(int argc, char *argv[]){
    int f1, f2, n;
    char buf[BUFSIZ];
    if (argc!=3){
        fprintf(stderr, "Usage:cp from to"); exit(1);
    }
    if ((f1=open(argv[1], O_RDONLY, 0))==-1){
        fprintf(stderr, "cp: can't open %s",argv[1]); exit(1);
    }
    if ((f2=creat(argv[2], PERMS))==-1){
        fprintf(stderr, "cp:can't create %s, mode %03o", argv[2], PERMS); exit(1);
    }
    while ((n=read(f1, buf, BUFSIZ))>0){
        if (write(f2, buf, n)!=n){
            fprintf(stderr, "cp:write error on file %s", argv[2]); exit(1);
        }
    }
    exit(0);
}

random access - Lseek