본문으로 바로가기

[C언어] 파일 입출력

category C 2014. 6. 4. 11:27

Stream 이란?

   스트림(Stream)이라는 용어는 흐름을 의미한다. 마치 개울가의 물들이 흘러가듯이 바이트들이 순서대로 입출력되는 논리적인 장치를 스트림이라고 한다. 파일에도 바이트들이 저장되어 있으며 읽을 때나 쓸 때 순서대로 바이트들이 입출력되므로 스트림이라고 할 수 있다. 키보드, 화면, 프린터 등의 물리적인 장비들도 바이트들이 순서대로 흘러 다니므로 일종의 스트림이다.

   대부분의 운영체제는 키보드와 화면(Console), 프린터 등을 스트림이라는 동질적인 장치로 다루며 파일과 같은 방법으로 입출력한다. 파일과 키보드, 화면 등의 장치는 서로 제어하는 방법이 다르지만 스트림이라는 논리적으로 동등한 장치로 표현되기 때문에 동일한 방법으로 입출력할 수 있다. 가령 파일에 쓰듯이 콘솔에 쓰면 화면에 문자열이 출력되며 같은 방법으로 프린터로 출력하면 문서가 인쇄된다.

   스트림은 내부에 입출력 버퍼를 가지고 있으며 이 버퍼는 스트림에 의해 자동으로 관리되므로 프로그래머는 버퍼를 준비하거나 관리할 필요가 없다. 어떤 스트림으로부터 얼마만큼의 데이터를 읽거나 쓰고 싶다는 최소한의 의사 표현만 하면 필요한 동작은 스트림이 내부적으로 알아서 수행한다. 그래서 스트림을 통한 입출력 방법은 사용하기 쉬우며 그래서 고수준이라고 하는 것이다.

   스트림의 현재 상태는 FILE 구조체에 기억된다. 이 구조체는 stdio.h에 다음과 같이 정의되어 있으며 구조체의 멤버들은 운영체제에 따라 조금씩 달라지기도 한다. <인용: winapi.co.kr>

 

이름     설명         버퍼

stdin      표준 입력    사용
stdout    표준 출력    미사용
stderr     표준 에러    미사용 

<예제>

void main()
{
     char buf[256];
     fputs("문자열을 입력해 보시오 : ",stdout);
     fgets(buf,256,stdin);
     fputs(buf,stderr);
}

<예제> 기본입출력(http://cafe.naver.com/busanc/21)

 

#include <stdio.h>
void main()
{
    int a;
    char b;
    char name[10];
    scanf("%d%c%s", &a, &b, name);
    printf("a = %d  b = %c  name = %s\n", a, b, name);
}

#include <stdio.h>
void main()
{
    int a;
    char b;
    char name[10];
    scanf("%d", &a);

    fflush(stdin); // 입력버퍼의 내용을 지운다.

    scanf("%c", &b);

    scanf("%s", name);
    printf("a = %d  b = %c  name = %s\n", a, b, name);
}


FILE 구조체

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

 

fopen

    Open a file.

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

filename

파일이름의 문자열, main 함수가 있는 파일과 같은 디렉토리에 있어야 하며,

틀릴경우 상대적인 디렉토리 경로명 혹은 절대적인 디렉토리 경로명을 지정해야한다.

예)

아래와 같이 위치가 틀릴 경우

<상대적인 디렉토리 경로명>

c:\Work\ExOpenFile\man.cpp 

c:\Work\a.txt

 

#include <stdio.h>
void main()
{
    FILE *pStream = fopen("..\\a.txt", "rt");
//    FILE *pStream = fopen("../a.txt", "rt");
    if (pStream)
        printf("파일 열기 성공 (o)\n");
    else
        printf("파일 열기 실패 (x)\n");
    if (pStream)
        fclose(pStream);
}

 

c:\Work\ExOpenFile\man.cpp 

c:\a.txt

   FILE *pStream = fopen("..\\..\\a.txt", "rt");
// FILE *pStream = fopen("../../a.txt", "rt");
 

c:\Work\ExOpenFile\man.cpp 

c:\Work\ExOpenFile\Text\a.txt

   FILE *pStream = fopen(".\\Text\\a.txt", "rt");

// FILE *pStream = fopen("./Text/a.txt", "rt");

 

c:\Work\ExOpenFile\man.cpp 

c:\Work\ExOpenFile\Text\English\

   FILE *pStream = fopen(".\\Text\\English\\a.txt", "rt");
// FILE *pStream = fopen("./Text/English/a.txt", "rt");

 

<절대적인 디렉토리 경로명>

   FILE *pStream = fopen("c:\\Work\\ExOpenFile\\Text\\English\\a.txt", "rt");
// FILE *pStream = fopen("c:/Work/ExOpenFile/Text/English/a.txt", "rt");

   FILE *pStream = fopen("c:\\a.txt", "rt");
// FILE *pStream = fopen("c:/a.txt", "rt");

 

리소스 파일들은 실행파일(exe)을 중심으로 상대경로명을 사용하는 것이 일반적이다. 리소스 파일들은 실행파일이 있는 위치에서 하위폴더로 형성되며 실행파일과 리소스 파일 폴더들이 함께 복사 되거나 이동 되기 때문에 상대경로명이 절대경로명보다 더 많이 쓰인다. 하지만 실행파일 위치와 상관 없이 특정한 곳에 리소스 파일을 두어야 한다면 절대경로명을 써야한다.

 

mode

r   read-only   파일이 존재하지 않으면 파일을 생성하지 않는다.           위치: 시작

w  write-only   파일이 존재여부와 상관 없이 파일을  새로 생성한다.      위치: 시작

a  append      파일이 존재하지 않으면 파일을 새로 생성한다.               위치: 끝

     can't read 읽을수 없으며 끝부분에 이어쓰기만 가능하다.   

 

r+  read&write 파일이 존재하지 않으면 파일을 생성하지 않는다.            위치: 시작

w+ read&write 파일이 존재여부와 상관 없이 파일을 새로 생성한다.        위치: 시작

a+  append    파일이 존재하지 않으면 파일을 새로 생성한다.                위치: 끝

     can read  읽을수 있으며 쓰기도 가능하다 r+다른점은 위치가 끝이다.

---------------------------------------------------------

b  binary

t   text

 

Return Value

열기에 성공한 FILE* 파일포인터가 반환되며, NULL 은 에러를 의미한다.

 

EOF

(End of file) 문자열의 끝을 표시하는 NULL(0)이 있듯이,

파일의 끝을 표시하는 EOF 가 있다. 값은 -1 이다.

#define EOF     (-1)

 

 

feof

파일의 끝(EOF)이면 Nonzero를 리턴한다. 끝이아니면 0 를 리턴한다.

int feof(FILE* stream );


fgetc

문자하나를 파일로부터 입력받는다.

int fgetc(FILE* stream );

 

fputc

문자하나를 파일에 출력한다.

int fputc(int c, FILE* stream );

 

fgets

문자열을 파일로부터 입력받는다.

char *fgets(char* string, int n, FILE *stream );

 

fputs

문자열을 파일에 출력한다.

int fputs( const char* string, FILE* stream );

 

fscanf

scanf와 동일한 방식으로 파일로부터 입력받는다.

int fscanf( FILE* stream, const char* format [, argument ]... );

 

fprintf

printf와 동일한 방식으로 파일에 출력한다.

int fprintf( FILE* stream, const char* format [, argument ]... );

고급함수

fread

바이트 수 만큼 파일로 부터 버퍼에 저장한다.

size_t fread( void* buffer, size_t size, size_t count, FILE* stream );

 

fwrite

바이트 수 만큼 파일에 버퍼의 내용을 출력한다.

size_t fwrite( const void* buffer, size_t size, size_t count, FILE* stream );

 

fseek 

파일포인터의 위치를 조정한다.

SEEK_SET: 시작 위치에서 offset 만큼 이동

SEEK_CUR: 현재 위치에서 offset 만큼 이동

SEEK_END: 끝 위치에서 offset 만큼 이동

offset 값이 양수(+)이면 위치가 끝 방향

offset 값이 음수(-)이면 위치가 시작 방향

int fseek( FILE *stream, long offset, int origin );

 

ftell
현재 위치를 알려준다.

long ftell( FILE *stream );

 

rewind

위치를 시작위치로 보낸다. fseek(pStream,0,SEEK_SET)와 동일하다.

void rewind( FILE *stream );

 

<실습>

학생들의 이름, 국어, 영어, 수학 점수를 파일로 부터 입력받고,

이름, 국어, 영어, 수학, 총점, 평균, 등수 순으로 파일에 출력한다.

 

<활용예제>

파일이 존재하면 이어서 계속 로그를 갱신하고,

파일이 존재하지 않으면 새로운 파일에 로그를 갱신한다.

#include <stdio.h>
#include <time.h>

void GetCurrentTime(char *szTime)
{
     time_t t;
     time(&t);
     strftime(szTime,128,"%Y-%m-%d/%H:%M:%S",localtime(&t));
}

void ErrorMsgLog(char *szMsg)
{
    FILE *pStream = fopen("Error.txt", "r+t");
    int iNumber, iCount = 0, iResult;
    char szMsgTemp[256], szTime[32];
    if (!pStream)
    {
        pStream = fopen("Error.txt", "w+t");
    }

    if (pStream)
    {
        printf("파일 열기 성공 (o)\n");
        do 
        {
            iResult = fscanf(pStream, "%d%s%s",&iNumber, szTime, szMsgTemp);
            ++iCount;
        }
        while (iResult != EOF);
        fseek(pStream, 0, SEEK_END);
        GetCurrentTime(szTime);
        fprintf(pStream,"%5d %20s %s\n", iCount, szTime, szMsg);
        printf("%5d %20s %s\n", iCount, szTime, szMsg);
    }
    if (pStream)
        fclose(pStream);
}

void main()
{
    ErrorMsgLog("에러났어요");
}