IT 과학/C언어

C언어 | 파일처리

곰뚱 2020. 3. 1.

 

 

 

표준 입출력과 입출력 방향 전환기능을 쓰면 getchar/putchar 등에 따라 범용적인 파일 처리가 행해진다. 먼저 이 방법에 관해서 설명한다. 다음에 버퍼 파일 입출력 함수와 저수준 파일 입출력 함수를 사용한 시스템 명령을 만드는 방법을 설명하고, 마지막으로 순차(sequential) 파일 작성법에 관해서 설명한다.

 

 

1. 표준 입출력과 I/O  방향 전환

(1) 표준 입·출력

Turbo C가 지원하는 표준 입·출력 파일에는 표준 입력, 표준 출력, 표준 에러 출력의 3종류가 있으며 이들은 프로그램 시작 때에 표 1에 표시한 파일 포인터 및 파일 디스크립터로 자동 오픈 된다. 따라서 사용자는 이들의 표준 입·출력을 자유롭게 사용할 수 있다. 예를 들면 getc/putc를 사용하여 표준 입·출력으로 액세스하는데는 다음과 같다.

    getc(stdin)   ···stdin에서의 1문자 입력. getchar()와 같다.
    putc(c,stdout)···stdout에 문자 c를 출력. putchar(c)와 같다.
    putc(c,stderr)···stderr에 문자를 c 를 출력

 

표 1 표준 입출력

파일

파일 포인터

파일 디스크립터

장치

표준 입력

stdin

0

키보드

표준 출력

stdout

1

화면

표준 에러

stderr

2

화면

 

(2) 입출력 방향 전환

UNIX나 MS-DOS에서는 키보드나 화면(CRT) 등의 디바이스도 파일의 일종으로 생각해서 일반 디스크 파일과 동등한 방식으로 처리할 수 있도록 되어 있다. 그리고 디바이스 또는 디스크에서의 입력을 행하는 표준 입력으로서 stdin, 출력을 행하는 표준 출력으로서  stdout을 정하여 사용자가 이 stdin/stdout을 실제의 디바이스 또는 디스크 파일에 할당되도록 되어 있다. 이것을 입출력 방향 전환 (I/O redirection)이라고 한다. 

 

stdin과 stdout은 보통 키보드나 화면에 각각 할당되어 있지만 명령행(command line)에서 다음 기호를 이용하여 입출력 방향 전환을 할 수 있다.

< …… 입력 방향 전환
> …… 출력 방향 전환

예를 들어

A>type a.c>b.c

라고 표시하면 본래 a.c의 내용이 화면에 출력되지만 이것이 디스크 파일 b.c 로 방향 전환되어 파일 b.c 에 a.c 의 내용이 출력된다.

 

표준 입력(stdin)에서 데이터를 입력하고 표준 출력(stdout)으로 데이터를 출력시키는 형의 프로그램은 입출력 방향 전환이 가능하므로 키보드와 화면에만 한정되지 않는 범용적인 파일 처리 프로그램이라고 한다.

 

예를 들어 stdin/stdout 에 대해서 입 .출력을 행하는 getchar/putchar을 사용하여 다음 프로그램을 생각해 보자.

 

  #include <stdio.h>
  main( )
  {
              int c;
              while ((c=getchar( )) != EOF)   /* stdin */
                  putchar(c);                /* stdout */
   }

 

이 프로그램(예를 들어, stdgilt 라는 이름으로 만들어졌다고 하자)은 입출력 방향 전환을 하지 않으면 키보드로 입력해서 화면으로 출력하는 아무 변화도 없는 프로그램이지만 다음과 같이 입출력 방향 전환하면

 

A> stdfilt < a.c

 

이것은 파일 a.c 의 내용이 화면으로 출력되는 형의 명령이 된다.
  

A> stdfilt > b.c

 

이라고 입출력 방향 전환하면 키보드에서 입력한 문자를 파일 b.c 에 작성하는 출력이 이루어져 텍스트 파일이 형성된다.

 

A > stdfilt <a.c> b.c

 

라고 입출력 방향 전환하면 a.c 의 내용을 b.c 에 복사하는 copy 명령이 된다.

 

이와 같이 stdin/stdout 으로의 입ㆍ출력을 실행하는 프로그램과 입출력 방향 전환을 다시 설정하는 것에 의하여 범용적인 프로그램을 작성할 수 있다.

 

프로그램의 실행 파일이 test.exe라 할 때 다음과 같이 실행해 보자.

                C> test
                C> test < c:\autoexec.bat
                        입력 방향을 파일 c:\autoexec.bat로 전환
                C> test > newfile.txt
                        출력 방향을 파일 newfile.txt로 전환
                C> test < c:\autoexec.bat > newfile.txt
                C> test > newfile.txt < c:\autoexec.bat
                        입-출력 방향을 모두 파일로 전환

        ※ 방향전환 문자의 양쪽에 있는 blank는 없어도 된다.

 

(3) 필터와 파이프

stdin에 입력하여 그 데이터를 조작한 후 stdout로 출력하는 형의 프로그램을 필터형 프로그램이라고 부른다. 필터형 프로그램은 파이프를 이용하여 얼마든지 연결해 갈 수 있다. 파이프는 UNIX와 MS-DOS 의 명령행으로 | 의 기호로 표시한다.

소문자를 대문자로 변환시키는 필터인 toupper를 다음에 표시한다. 이 필터를 사용해서 a.c 의 파일을 대문자로 변환시키고 b.c 에 복사하는데는 다음과 같이 명령행에서 입력한다.

 

A> toupper < a.c > b.c

 

다음은 소문자를 대문자로 변환하는 필터 toupper이다.

 

        #include <stdio.h>
         #include <ctype.h>
         main ( )
         {
              int c;
              while ((c=getchar( )) != EOF)
                       putchar(toupper(c));
         }

 

다음은 텍스트를 1행 단위로 읽어서 네 자리의 행 번호를 각 라인의 앞에 추가하는 필터 line이다.

      #include <stdio.h>
      main( )
     {
          int count=1;
          char buf[256];
          while (fgets(buf,256, stdin) != NULL)
                    printf("%04d : %s",count++,buf);
     }

 

그러면 앞에 표시한 두 개의 필터 toupper 와 line을 이용하여 파일 a.c 의 내용을 대문자로 변환시키고 행번호를 붙여서 파일 b.c 에 출력하려면 다음과 같이 해야한다.

        A>type a.c | touper | line > b.c
        D>type a.c | toupper | line > b.c

       D>type b.c
       0001:  #INCLUDE <STDIO.H>
       0002:  MAIN( )                        /* C SAMPLE PROGRAM */
       0003:  {
       0004:      INT C;
       0005:      WHILE ((C=GETCHAR()) != EOF){
       0006:            upper(C);
       0007:      }
       0008  }
       0009:  upper(C) /* USER FUNCTION */
       0010:  INT c;
       0011:  {
       0012:      IF ('A'<=C && C<='Z')
       0013:             C=C-('A'-'A');
       0014:      PUTCHAR(C);
       0015:  }

728x90

 

 

2. 시스템 명령 작성

파일 입출력 함수를 사용해서 DOS의 TYPE 명령이나 COPY 명령을 작성해보자. 시스템 명령에서는 명령행에서 파일명 등을 파라미터로 입력하게 되지만 그 방법은 지난 강의들을 참조하기 바란다.

 

예를 들어, TYPE 명령을 만들었다고 가정하고 그 프로그램명을 MYTYPE로 하여 명령행에서 다음과 같이 실행하는 것으로 하자. 

 

A>mytype a.c

 

이것에 의하여 명령행의 파일명 a.c는 프로그램 중에서 argv[1]로 참조할 수 있기 때문에 파일 a.c를 오픈하려면

 

fopen(argv[1], "r");

 

라고 표시한다.

 

(1) getc를 이용한 TYPE 명령

파일 fp에서 1문자를 읽고 그것을 화면에 출력한다. 이 과정을 끝까지 반복하면 getc를 이용한 TYPE 명령이 완성된다.  

명령행에서는

          A>mytype a.c

이와 같이 입력하면 된다.

 

예제 1. getc를 사용해서 TYPE 명령을 작성하여라.

*프로그램

      #include <stdio.h>
      main(argc, argv)                           /* A>mytype filename.typ */
      int argc;
      char *argv[];
      {
                 FILE *fp;
                 int c;
                 if ((fp=fopen(argv[1], "r"))==NULL) {
                      printf("Can't open File\n");   
                      exit();  
                 }
                 while  ((c=getc(fp))!=EOF)
                     putchar(c);  
                 fclose(fp);  
     }

 

(2) getc/putc를 이용한 copy 명령

파일 fpi에서 1문자를 읽고 그것을 파일 fpo에 출력한다. 이것을 파일 끝까지 반복하면 getc/putc를 이용한 copy 명령을 완성된다.  

명령행에서는

         A>mycopy a.c b.c

이와 같이 입력하고 a.c에서 b.c로 복사하는 것으로 한다.

 

예제 2. getc/putc을 사용해서 copy 명령을 작성하여라.

*프로그램

     #include <stdio.h>
     main(argc, argv)        /* A>mycopy source destinate */
     int argc;
     char *argv[];
     {
            int c;
            FILE *fpi, *fpo;
            if (argc!=3){
                printf("Arg is not correct\n");
                exit();
            }
            if ((fpi=fopen(argv[1],"r"))==NULL){
                 printf("Can't open File\n");
                 exit();
            }
            if ((fpo=fopen(argv[2],"w"))==NULL){
               printf("Can't create File\n");
                 exit();
            }
            while ((c=getc(fpi))!=EOF)
                 putc(c,fpo);
            fclose(fpi);
            fclose(fpo);
     }

 

(3) 행번호가 있는 TYPE 명령

fgets을 사용하여 파일로부터 1행단위로 읽고 행번호를 붙여서 화면에 표시해 가는 것으로, 행번호가 붙은 TYPE 명령이 된다.  

명령행에서는

 

A>list a.c

 

라고 입력한다.

 

예제 3. fgets을 사용하여 행번호 붙은 TYPE 명령을 작성하여라.

* 프로그램

    0001;  #include <stdio.h>
    0002;  main(argc, argv)                              /* A> list filename.typ */
    0003;  int argc;
    0004;  char *argv[ ];
    0005;  
    0006;         int count=0;
    0007;         char buf[256];
    0008;         FILE *fp
    0009;             if ((fp=fopen(argv[1],"r"))==NULL) {
    0010;                 printf("Can't open File\n");
    0011;                 exit( );
    0012;
    0013;         while (fgets(buf,256,fp) != NULL)
    0014;             printf("%04d:\t%s",++count,buf);
    0015;         fclose(fp);

이 리스트는 list 자신에 의하여 처리된다.

 

(4) 16 진수 덤프 명령

파일의 내용을 16진수로 출력하는 덤프(dump) 명령을 작성하여 보자. 16바이트 단위로 화면에 표시해 가는데 read함수를 사용해서 파일에서 read해 가기로 한다. 화면상에 표시되는 필드는 주소(어드레스) 필드, 16진수 필드, ASCII 필드의 세 가지로 분류한다.  

명령행에서

        A>fdump  a.c

라고 입력한다.

 

예제 4. read를 사용해서 16진수 dump 명령을 작성하여라.

* 프로그램

  #include <stdio.h>
  #include <io.h>
  #include <fcutl.h>
  main (agrc, argv)
  int argc;
  char *argv[ ];
  {     
         int fdi,i,n,adr;
         unsigned char buf[16];    /* ① */
         if ((fdi=open(argv[1],O_RDONLY | O_BINARY))==-1) {    /* ② */
             printf("%s\n",argv[1]);
             exit( );
         }
         adr=0;
         while ((n=read(fdi,buf,16)) != 0) {    /* ③ */
             printf("%04x: ",16*adr++);    /* ④ */
             for (i=0;i<n;i++)    /* ⑤ */
                  printf("%02x",buf[i]);
             for (i=0;i<n;i++) {    /* ⑥ */
                     if (buf[1]<' '||'∼'<buf[i])
                         putchar('.');
                     else
                         putchar(buf[i]);
                  }
                  putchar('\n');
               }
               close(fdi);
  }
 

* 해설

①  char 형으로 버퍼를 선언하면 0x80 이상의 코드는 부호가 붙는 것으로 하고 4바이트로 확장되며 printf ("%02x", buf[i] )는 4자리로 출력된다.  그러므로 unsigned char 형으로 선언해야 한다.
②  파일을 2진 모드로 오픈 한다. 만약에 텍스트 모드로 오픈 하게 되며 CR·LF ↔'\n' 변환에 의하여 정확히 16바이트를 읽어들일수 없는 경우가 생긴다.
③  파일 fdi에서 buf로 16바이트를 read 한다.
④  adr은 어드레스값을 표시하기 위한 카운터이다. %04x 에서 0에 의하여 비어 있는 좌측으로 0이 채워진다.
⑤  buf 의 16바이트의 내용을 16진수 두 자리로 표시한다.
⑥  buf 의 16바이트의 내용을 ASCII 문자로 표시한다. 이 때 ' ', '∼'의 출력 가능한 문자는 출력 가능하지만 그 이외의 문자는 '.'으로 표시한다.

 

* 결과

    0000: 23 69 6e 63 6c 75 64 65 20 3c 73 74 64 69 6f 2e #include <stdio.
    0010: 68 3e 0d 0a 6d 61 69 6e 28 29 09 09 2f 2a 20 43 h>..main( )../* C
    0020: 20 53 61 6d 70 6c 65 20 50 72 6f 67 72 61 6d 20 Sample Program
    0030: 2a 2f 0d 0a 7b 0d 0a 09 69 6e 74 20 63 3b 0d 0a  */..{...int c ; ..
    0040: 09 77 68 69 6c 65 20 28 28 63 3d 67 65 74 63 68 . while ((c=getch
    0050: 61 72 28 29 29 21 3d 45 4f 46 29 7b 0d 0a 09 20 ar( )) ! =EOF) {...
    0060: 20 20 20 75 70 64 69 73 70 28 63 29 3b 0d 0a 09   upper(c) ; ...
    0070: 7d 0d 0a 7d 0d 0a 75 70 64 69 73 70 28 63 29 09 }..}..upper(c).
    0080: 2f 2a 20  55 73 65 72 20 46 75 6e 63 74 69 6f 6e  /* User Function
    0090: 20 2a 2f 0d 0a 69 6e 74 20 63 3b 0d 0a 7b 0d 0a   */..int c ; .. {..
    00a0: 09 69 66 20 28 27 61 27 3c 3d 63 20 26 26 20 63 .if('a'<=c && c
    00b0: 3c 3d 27 7a 27 29 0d 0a 09 20 20 20 20 63 3d 63 <='z')... c=c
    00c0: 2d 28 27 61 27 2d 27 41 27 29 3b 0d 0a 09 70 75 -('a'-'A') ;... pu
    00d0: 74 63 68 61 72 28 63 29 3b 0d 0a 7d 0d 0a 1a  tchar(c) ; ..}...

 

 

 

3. 순차(sequential) 파일

(1) 파일 구성

파일을 구성 방법으로 분류하면 순차 파일과 랜덤 파일 (random file)로 분류된다.즉, 순차 파일은 바이트 단위인 경우는 일반적으로 말하는 텍스트 파일을 의미하고, 레코드 단위인 경우는 일반적으로 말하는 순차 파일을 의미한다. 순차 파일은 파일의 선두로부터의 순서적인 read/write가 행해지고 랜덤 파일 파일은 임의의 위치에서 read/write가 행해진다.

 

(2) 순차 파일의 write

예를 들어 이름과 나이의 데이터가 표 2와 같다고 가정하자.

 

표 2 이름과 나이의 데이터

이름

 연령

Ann

18

Candy

21

Nancy

19

:

:

 

이와 같은 형의 데이터를 순차파일로서 write 하려면 fprintf를 사용해서 다음과 같이 한다.

fprintf(fp, "%s %d \n", name, year);   

 

예제 5. 표 2의 데이터를 입력하고 이것을 순차 파일로 작성하여라. 필드의 구분은 ' ' 과 '\n'을 사용하라.

* 프로그램

       #include <stdio.h>
       main (argc, argv)
       int argc;
       char *argv[ ];
       {
              FILE *fp;
              int year;
              char name[20];
              
              if ((fp=fopen(argv[1],"w")) == 0) {
                  printf("can't create %s\n", argv[1]);
                  exit( );
              }
              printf("name year\n");
              while (scanf("%s %d", name, &year) != EOF)
                  fprintf(fp, "%s %d\n", name, year);
              fclose(fp);
       }

 

*  결과

      A>d:seqwrite d:a.txt
      name           year
      Kim Chang Hwa 37
      Kim Yoon Sung 5
      Lee Mi Ja 48
      ^Z

 

(3) 순차 파일의 read

fprintf에서 순차 파일로 write하는 데이터를 read하려면 fscanf을 사용해서 다음과 같이 한다.

fscanf(fp, "%s %d", name, &year);

 

예제 6. 예제 5에서 write한 파일을 read하고 데이터를 화면에 표시하여라.

* 프로그램

      #include <stdio.h>
      main (argc, argv)
      int argc;
      char *argv[ ];
      {
               FILE *fp;
               int year;
               char name[20];
              if  ((fp=fopen(argv[1],"r"))==0) {
                   printf("can't open %s\n", argv[1]);
                   exit( );
              }
              while (fscanf(fp, "%s%d", name, &year) != EOF) {
                         printf("%20s%3d\n" ,name, year);
              }
              fclose;
      }

 

* 결과

     A>d:seqread d:a.txt
         Lee Hyo Lee 23
         Sung Yoo Lee 20
         Ohk Joo Hyun 21

 

 

 

그리드형

댓글