18장. 파일 입출력

 

1. 스트림파일

   

 

 - 스트림파일은 프로그램과 입출력장치 사이를 연결하는 논리적인 파일이다

 - 스트림파일은 프로그램에게 파일포인터를 반환한다(프로그램이 이를 이용해 장치에 접근)

 - 스트림파일과 I/O간의 연결은 OS가 담당한다

 - 스트림파일은 문자배열형태의 버퍼와 FILE구조체로 이루어져 있다

 - FILE구조체는 데이터의 위치, 버퍼메모리의 위치와 크기등이 들어있다(578.p상단 참조)

 - 버퍼는 프로그램이 출력한 데이터, 혹은 입력장치에서 한번에 많은 데이터를 읽어놓는다

 - 위와 같은 구조로 인해 스트림파일을 사용하면 프로그램과 I/O간의 통신에서 r속도와 w속도간 차이를 줄일 수 있다.

 

2. 표준입출력 스트림파일

 - OS는 기본적으로 3개의 스트림파일을 개방한다. 그리고 우리가 입출력할때 기본적으로 이 3개의 스트림파일과 연결된다

   

※ 입력시 ctrl+z를 입력하면 해당함수가 EOF를 반환하며, 프로그램이 종료된다(리눅스, 유닉스에서는 컨+d)

 

3. 파일 개방과 폐쇄

 - 파일열기

    원형: FILE *fopen(const char *, const char *);  // fopen(디렉+파일명[혹은 파일명], 개방모드);

    ex)

      FILE *fp;

      fp=fopen("a.txt","r,");

      => 성공시 fp에 파일포인터를 반환, 실패시 NULL포인터 반환

      => fopen함수가 파일을 찾는 위치는 현재위치이다

      => 디렉터리를 포함할 때는 \를 2번해야한다(\는 제어 문자 사용시, \\는 디렉터리를 나타낸다)

 

   

 

 - 파일닫기

    원형: int fclose(FILE *);

    ex)

      fclose(fp);

      => 성공시 0반환, 에러시 EOF(-1을 의미한다)를 반환

      => 스트림버퍼를 지우고 파일을 닫는다

 

 - 텍스트 파일과 바이너리파일

    파일은 데이터에 따라 text파일과 bin파일(그림, 실행파일 등)로 구분한다

      (유닉스에서는 구분하지않고 모두 바이너리 파일로 취급한다)

    개방시 텍스트는 't', 바이너리는 'b'를 추가해야한다

      (옵션을 추가하지 않으면 텍스트로 취급한다)

    시스템에 따라 CR, LF 인식방식이 다르기 때문에 주의(589~590.p참조)

   

 - +개방모드

    개방모드에 +를 붙일 수 있다

    ex)

     - r+(rb+) : (바이너리)파일에 읽고 쓰기

     - w+(wb+) : (바이너리)파일에 지우고 쓰거나 읽기

     - a+(ab+) : (바이너리)파일에 파일 끝에 추가하거나 읽기

    fseek: 파일 개방후 입력, 출력을 전환할 때 사용

    rewind: 위치 지시자를 맨처음으로 설정

    feof: 스트림파일의 데이터를 모두 읽었는지 확인

 

4. 문자입출력함수

 - fgetc

    문자입력함수(파일에서 프로그램으로)

    원형: int fgetc(FILE *); //반환형이 int인게 특징

    파일의 데이터를 다 읽으면 EOF(-1)을 반환한다

    파일에서 버퍼크기만큼 데이터를 가져와 버퍼에 저장

    문자를 1개씩 읽을때 마다 위치지시자가 1씩증가

 

 - fputc

    문자출력함수(프로그램에서 파일로)

    원형: int fputc(int, FILE *); //파일포인터를 입력하지 않을시 stdout로 출력

    입력된 문자를 스트림파일의 버퍼로 출력하며, 해당문자를 반환한다

    모든문자를 출력하면 EOF(-1)을 반환한다

    스트림파일의 버퍼는 ①개행문자('\n')를 인식, ②새로운 입력시작, ③버퍼가 가득찰때 해당 파일로 출력한다

 

 - ex)

    FILE *fp;

    int ch;

    fp=fopen("a.txt","r");

    ch=fgetc(fp);

    fclose(fp);

 

    FILE *fp;

    char str[]="banana";

    int i=0;

    fp=fopen("b.txt","w");

    fputc(str[i],fp);

    i++;

    fputc('\n',fp);

    fclose(fp);

 

 

5. 문자열 입출력함수

 - fgets

    문자열 입력

    원형: char *fgets(char *, int, FILE *); //인자는 차례대로 입력받을 배열명(주소), 배열크기, 읽을파일

    '\n'(개행)까지 한번에 읽고 끝에 '\0'(널)을 붙인다.(결과적으로 개행-널 순으로 끝나게 된다)

    파일에 입력받을 데이터가 없으면 NULL반환(EOF가아니다!)

    만일 입력받을 배열의 크기보다 데이터의 크기가 크면, 배열에는 '배열크기-1'개 문자들과 '\0'가 저장된다

 - fputs

    문자열 출력

    원형: int fputs(const char *, FILE *); //인자는 차례대로 출력할 문자열, 출력파일

    '\0'이전까지 출력하며, '\n'가 없으면 자동으로 줄을 바꾸지 않는다

    파일을 더이상 읽을 수 없으면 EOF(-1)을 반환(NULL이 아니다!)

 => 문자열 입출력 함수에 stdin, stdout을 파일포인터로 줄 수 있다.

 

 - ex)

    FILE *ifp, *ofp;

    char str[80];

    ifp=fopen("a.txt","r");

    ofp=fopen("a.txt","w");

    fgets(str, sizeof(str), ifp);

    str[strlen(str)-1]='\0'; //개행문자 위치에 널문자 입력(strlen은 배열에 저장된 문자열의 수를 세는함수로서 널문자 이전까지의 문자의 수를 센다)

    fputs(str, ofp);

    fputs(" ",ofp);

    fclose(ifp);

    fclose(ofp);

 - gets와 puts대신 fgets와 fputs를 사용하는것이 좋다(허용되지 않은, 할당하지 않은 메모리 공간 침범 예방)

   

 

6. 여러가지 자료형을 한번에 입출력하는 함수

 - scanf와 printf랑 비슷하지만 파일을 지정할 수 있다

 - fscanf

    원형: int fscanf(FILE *, const char *, ...); //인자는 각각 파일포인터, 입력변수의 자료형, 해당변수

    파일에서 데이터를 읽을 때는 char배열로 읽지만, 변수에 저장할 때는 해당 자료형에 맞추어 저장한다

    다 읽으면 EOF반환

    변수간 구분은 공백, 탭, 개행문자로 한다

 

※ 개행, 공백, 탭같은 문자를 '화이트 스페이스'라고 한다

 

 - fprintf

    원형: int fprintf(FILE *, const char *, ...); //인자는 각각 파일포인터, 출력변수의 자료형, 해당변수

    변수의 데이터를 문자열로 변환하여 char배열로 저장하고, 그대로 출력할 파일에 저장한다.(즉 문자로 저장된다)

    출력을 완료하면 출력한 문자의 바이트 수를 반환하며, 에러시 EOF를 반환한다

    파일에 개행을 입력하고 싶으면 printf와 똑같이 '\n'을 입력한다

 

 - ex)

    FILE *ifp, *ofp;

    char name[20];

    int kor, eng, mat;

    ifp=fopen("a.txt","r");

    ofp=fopen("a.txt","w");

    fscanf(ifp, "%s %d %d %d", name, &kor, &eng, &mat);

    fprintf(ofp, "%s %d %d %d", name, &kor, &eng, &mat);

    fclose(ifp);

    fclose(ofp);

 

 

※ 버퍼공유문제

 - fscanf는 버퍼에서 데이터를 가지고 올때, 처음 의미있는 글자가 나오기 이전까지의 글자(공백, 개행, 탭)를 무시하고, 의미있는 글자부터 구분문자 '이전'까지 데이터를 가지고 온다.(데이터를 배열에 저장하고 끝에 널을 붙인다)

 - fgets는 버퍼에서 데이터를 가지고 올때, 처음부터 개행문자를 포함한 데이터를 가지고 온다(배열에 저장시 개행을 널로 변경)

 - 이런 입출력함수들을 혼용할 경우 버퍼공유문제가 생긴다

 - ex)

    버퍼내용: 1 7 \n a b c d  e f

    fscanf - fgets순으로 함수를 사용한다면

    fscanf는 17값이 저장되고 fgets는 '\n'만 저장된다

 - 해결법

    ①fscanf와 fgets사이에 fgetc함수를 넣어 개행문자를 제거한다(추가적인 문제가 생길 수 있음. 608.p 하단~ 609.p 상단 참조)

    ②fflush함수 사용

     - 원형: int fflush(FILE *);

        반환값은 0이며, 버퍼를 비우지 못할 때 EOF를 반환한다

        입력파일에 대해서는 표준이 정의되어있지 않다(몇몇 라이브러리에서는 파일 포인터와 연결된 스트링파일의 버퍼를 비운다)

        출력파일에 사용할 시, 출력파일의 포인터를 주면 버퍼에 남은 데이터를 장치로 바로 출력한다

    => 따라서 fflush를 사용하는것도 문제가 생길수 있다. 따라서 처음부터 설계를 잘하자

 

7. fread와 fwrite

 - 파일의 데이터(혹은 변수)형식과 상관없이 그대로 복사하여 저장한다(즉, 문자로 변환하는 과정이 없다.)

 - 변환과정이 없기 때문에 속도가 빠르고, 대용량 데이터 처리에 알맞다.

 - 데이터를 문자로 변환하지 않기 때문에 메모장같은 텍스트 편집기로 열수 없으며, 출력,입력하는 파일은 바이너리파일로 인식한다

 - 따라서 fopen시 바이너리 모드로 개방해야한다.(택스트모드로 개방시 개행문자의 처리방식이 달라 데이터의 크기(혹은 파일크기)가 달라지는 문제가 생긴다)

 

 - 원형

    size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);

     - 읽은 데이터 수를 반환한다, 인수는 각각 데이터를 저장할 장소(변수)의 주소, 변수의 크기, 개수, 파일포인터

    size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream);

     - 출력한 데이터 수를 반환한다, 인수는 각각 출력할 변수(데이터)의 주소, 변수의 크기, 개수, 파일포인터

    =>size_t는 unsigned int형을 typedef로 재정의한 자료형이고, 이는 stdio.h에 정의되어 있다.

 

 - ex)

    FILE *bfp;

    int num=10;

    int res;

    bfp=fopen("a.txt", "wb");

    fwrite(&num, sizeof(num), 1, bfp); //a.txt파일에 num값(10) 저장

    fclose(bfp);

    bfp=fopen("a.txt", "rb");

    fread(&res, sizeof(res), 1, bfp); //a.txt에 있는 데이터를 그대로 변수에 입력

    printf("%d", res); //값이 10으로 나온다

 

 

 

 

19장. 전처리와 분할 컴파일

 

1. 전저리지시자

 - 컴파일 과정중 전처리단계에서, 컴파일러는 전처리지시자를 참조하여 소스코드를 편집한다.

 

2. #include

 - 소스코드에 헤더파일(만아니라 모든 텍스트파일도 된다. 626.p참조)을 포함시키고자 할때

 - 종류

    #include <> : 기존 aPI경로에서 헤더파일을 검색

    #include "" : 임의의 경로에서 사용자가 작성한 헤더파일 검색

   

3. #define

 - 메크로 상수와 메크로 함수를 만들때 사용한다

 - 메크로명은 관례상 대문자 사용

 - 형식: #define 메크로명 치환될부분

 - 종류

    상수, 문장

    ex)

      #define PI 3.14

      #define INTRO "apple \  //치환될 부분이 길어 다음줄로 입력시 '\'로 연결한다

      & banana"

 

    함수

    ex)

      #define MUL(a,b) ((a)*(b))

      => 치환될 부분에 많은 괄호를 넣어야한다. 이유는 다음과 같다

       - a*b라면 30/MUL(2,5) -> 30/2*5

       - (a*b)라면 30/MUL(2,5+3) -> 30/(2*5+3)

 

4. 이미 정의된 메크로

 - __DATE__  :  컴파일을 시작한 날짜

 - __TIME__  :  컴파일을 시작한 시간

 - __FUNCTION__  :  현재 __FUNCTION__이 사용된 함수명

 - __LINE__  :  현재라인수, #line로 수정가능

 - __FILE__  :  디렉터리 경로를 포함한 파일명, #line로 수정가능

 - #line => 구성: 행번호 "파일명" (행번호는 현재라인의 번호를 행번호로 치환한다)

 

 

5. 메크로 연산자

 - 전처리 연산자라고도 한다. 즉, 전처리 지시자는 아니다

 - 종류

    #

     - 메크로 함수의 '인수를 그대로' 문자열로 치환

     - ex)

        #define PRINTF(x) printf(#x "=%d", x)

        PRINTF(10+20); => '10+20=30'이 출력됨

    ##

     - 메크로 함수의 두인수를 붙여서 하나의 인수로 치환

     - ex)

        #define NAME(x,y) (x##y)

        int a1;

        NAME(a,1)=10;

        printf("%d", a1); => 10이 출력된다

 

6. 조건부 컴파일 지시자

 - 조건식이 참이면 해당문장을 컴파일한다

 - 조건식에는 정수형 상수나 정수형으로 치환되는 매크로를 사용

    ex)

    #define VER 7

    #if VER >= 6

    ~~~

    #endif

 - 조건식을 적을 때 괄호는 생략가능하다

 - 컴파일할 문장이 두문장 이상이더라도 중괄호를 사용하지 않는다

 

 - 종류

    ①

      #if 조건식1 //조건식1을 만족하면 문장1 컴파일

        문장1

      #elif 조건식2 //그렇지않고 조건식 2를 만족하면 문장2 컴파일

        문장2

      #else //둘다아니면 문장3 컴파일

        문장3

      #endif  //조건부 컴파일 지시자 끝.

    ②

      #ifdef 매크로명 //if defined의 줄임말, 해당 매크로가 정의되어있으면 컴파일

        컴파일할 문장

      #endif

    ③

      #ifndef 메크로명 //if not defined의 줄임말, 해당매크로가 정의되어 있지않으면 컴파일

        컴파일할 문장

      #endif

    ④ 조건을 만족하지않아 컴파일 자체를 중단할 경우

      #if 조건식

        컴파일문장

      #else

      #error 버전6.0이상필요 //조건을 만족하지 않으면 에러메세지(버전6.0이상필요)를 띄우고 컴파일 종료

      #endif

    ⑤ 응용

      #if (defined(BIT16) && (VER>=6))

        컴파일 문장

      #endif

 - 조건부 컴파일은 프로그램의 호환성이 향상가능하다

    ex)

      #if 해당컴파일러 && OS

      #elif ~~

      .....

 

7. #pragma

 - 컴파일러의 컴파일 방법을 세부적으로 제어할 때 사용

 - 지시명(directive-name)을 통해 어떤기능을 제어할지 알려줌

 - 종류

    #pragma pack(push, 1) //바이트 얼라인먼트를 1로 바꿈(구조체의 패딩바이트 크기 변화)

    #pragma pack(1)  //바이트 얼라인먼트를 1로 바꿈(구조체의 패딩바이트 크기 변화)

    #pragma pack(pop) //이전의 바이트얼라인먼트 적용

    #pragma warning(disable:4996) //4996 경고메세지를 표시하지않음(vc++컴파일러는 함수의 안전성을 경호하는 메세지를 표현한다)

    => 자세한 내용은 컴파일러 메뉴얼을 참조한다

 

8. 분할 컴파일

 - 컴파일러는 소스단위로 컴파일을 하므로 다른 파일에 선언된 전역변수를 알 수 없다.(extern으로 이를 해결한다)

 - 기본적으로 둘이상의 파일에서 동일한 이름의 전역변수를 선언할 수 없다. 하지만 일부 컴파일러는 extern을 자동으로 선언하여 이를 해결한다(그냥 설계할때 extern을 선언하자)

 - extern

    '다른파일에 선언된' 전역변수를 사용(참조)하고자 할때 사용

 - static(정적 전역변수, 정적 지역변수가 아니다!)

    다른파일이 해당 전역변수를 참조하지 못하게 할때 사용(즉 전역변수를 해당 소스파일에서만 사용하고자 할때)

 

9. 헤더파일 중복문제

 - 헤더파일에는 구조체가 있기 때문에 헤더파일을 중복선언시 오류가 생긴다(구조체는 중복선언불가)

 - 이러한 중복문제를 해결하기 위해 조건부 컴파일 전처리 명령을 사용한다.

 - ex) point.h 헤더파일

    #ifndef _POINT_H_

    #define _POINT_H_

     //point구조체 선언

    #endif

    => 이렇게 만들면 헤더파일을 2번 불러와도 조건검사를 통해 중복선언을 하지 않게된다

 

=========

추가적인 지식

 

※ const를 사용한 변수:

 - 변수선언시 앞에 const를 붙이면 상수처럼 그값을 바꿀 수 없다

 - 따라서 선언과 동시에 초기화를 해야한다.

 - ex) const double pi=3.14;

 

※ 자료형은 예약어(reserved word 혹은 keyword), 변수명은 식별자(identifier)라고 한다

 

※ ANSI란?

 - 미국 표준 협회(American National Standards Institute, ANSI)는 미국의 산업 표준을 제정하는 기구이며, 여기서 제정된 표준을 또한 ANSI라고 부르기도 한다.

 - 즉 코딩에서의 ANSI는 미국 표준 협회에서 제정한 미국 표준 코드를 의미한다(미국에서 나아가 국제표준이 된 코드 규격)

 - 참조: https://ko.wikipedia.org/wiki/%EB%AF%B8%EA%B5%AD_%EA%B5%AD%EB%A6%BD_%ED%91%9C%EC%A4%80_%ED%98%91%ED%9A%8C

 

※ c99란?

 - 1990년대 후반에 추가적으로 개정된 규범을 흔히 c99이라 부른다(1999년, ISO/IEC 9899:1999출간)

 - c99은 기술적 교정에 의하여 현재까지 3번의 수정이 있었다.

 - 참조

    https://ko.wikipedia.org/wiki/C99

    https://blog.naver.com/tipsware/221032917097

 - 다음사이트에서 현재까지 개정된 c99을 살펴보고 다운로드 할 수 있다

    https://webstore.iec.ch/searchform&q=ISO%2FIEC%209899

 

※ C Programming FAQs 한국어번역

 - http://cinsk.github.io/cfaqs/index.ko.html 에서 다운로드할 수 있다.

 - 그밖에 ISO C와 ISO C++ 차이점(한글번역)등이 있다.

 

 

※ 찾다보니 이런곳도 있네(분야별 코딩책, 특허, 교육사이트)

    http://pcb4.tistory.com/1248

    https://patents.google.com/patent/KR101219535B1/ko

    https://learnitdeep.com/C%EC%96%B8%EC%96%B4-%EB%A7%8C%EB%93%A0%EC%9D%B4%EC%99%80-C%EC%96%B8%EC%96%B4%EC%9D%98%EC%97%AD%EC%82%AC.html

 


※ 변환문자 의미 자료형

1. %d : 10진수로 출력 정수형
2. %f : 실수형
3. %e : 지수형
4. %o : 8진수로 출력
5. %x : 16진수로 출력
6. %u : 부호없는 10진수로 출력
7. %g : 실수형으로 자동 출력
8. %p : 포인터의 주소를 출력
9. %c : 하나의 문자로 출력 문자형
10. %s : 문자열을 출력

출처: http://clanguage.tistory.com/1

'Study > 프로그래밍_언어' 카테고리의 다른 글

cpp - 후기  (0) 2022.10.07
시작  (0) 2020.07.22
C언어 보충5 - 16, 17장  (0) 2018.02.03
C언어 보충4 - 15장  (0) 2018.02.03
C언어 보충3 - 13, 14장  (0) 2018.02.02

+ Recent posts