728x90
반응형

파일 입출력

  • 파일 스트림

파일 입/출력을 위해서는 파일과 프로그램 사이에 입출력 스트림을 형성해야 한다. 파일은 운영체제에 의해서 그 구조가 결정되고 관리되는 대상이기 때문에, 파일 뿐만 아니라 스트림의 형성 또한 운영체제의 몫이다.

 

  • 파일 스트림의 형성 : fopen 함수와 FILE 구조체

fopen 함수는 파일과 프로그램 사이의 스트림을 형성하도록 하는 함수이다. 첫 번째 인자는 스트림을 형성할 파일의 이름(/출력의 대상 파일 경로), 두 번째 인자에는 형성할 스트림의 종류에 대한 정보를 문자열 형태로 전달(읽기/쓰기 모드 등)한다. 그리고 나서 이 함수는 첫 인자에 해당하는 파일과 스트림을 형성하고, 스트림 정보를 FILE 구조체 변수에 담아서 그 변수의 주소 값을 반환한다. FILE 구조체는 어떤 형태로 어떤 내용을 구성하고 있는지 알 필요가 없다. 단지 FILE 구조체에는 fopen함수가 호출 성공 시 해당 파일에 대한 정보가 담기고, 그러한 FILE 구조체 변수의 주소 값을 반환함으로써 FILE 구조체 포인터를 통해 가리킨다. FILE 구조체 포인터 변수는 파일(파일의 정보가 담긴 FILE 구조체 변수)을 가리키고 있는데 이러한 FILE 구조체 포인터를 이용해서 파일에 데이터를 저장하거나 파일에 저장된 데이터를 읽는다.

 

  • 입력 스트림과 출력 스트림 생성의 예

먼저, 스트림 생성 및 소멸에 필요한 함수 소개부터 진행한다.

스트림의 소멸을 요청하는 fclose 함수이다.

파일로 출력하는, 출력 스트림을 생성하는 예제

먼저, 8번 라인에서 파일 포인터를 생성하고, 10번 라인을 통해 fopen 함수를 호출함으로써 파일 출력 스트림을 생성한다. fopen 함수는 파일에 대해서 열지 못할 경우 NULL 포인터를 반환하므로 11번 라인에서 이에 대한 검사를 진행한다. (사실, 파일에 출력을 할 때에는 파일이 존재하지 않으면 파일을 생성하고 출력을 하기 때문에 딱히 검사를 하지 않아도 되긴 하다.) 그리고 16번 라인에서 fputc함수를 이용하여 ‘a’ 문자를 출력한다. 그리고 형성한 출력 스트림을 소멸 요청하는 fclose 함수를 통해 outputFilePointer에 해당하는 출력 스트림을 소멸한다. 그리고 그 결과 파일이 생성되었고, ‘a’ 문자가 잘 출력된 것을 확인할 수 있다.

 

파일로부터 입력하는, 입력 스트림을 생성하는 예제

다음은 위에서 생성한 파일 속 문자를 읽어오는 예제이다.

먼저, 8번 라인에서 파일 포인터를 생성하고, 11번 라인을 통해 fopen 함수를 호출함으로써 파일 입력 스트림을 생성한다. fopen 함수는 파일에 대해서 열지 못할 경우 NULL 포인터를 반환하므로 12번 라인에서 이에 대한 검사를 진행한다. 그리고 17번 라인에서 fgetc함수를 이용하여 문자 하나를 파일로부터 입력 받아온다. 그리고 형성한 입력 스트림을 소멸 요청하는 fclose 함수를 통해 inputFilePointer에 해당하는 입력 스트림을 소멸한다. 그리고 읽어온 ‘a’ 문자가 잘 출력된 것을 확인할 수 있다.

파일의 스트림에도 중간에 입출력 버퍼가 존재한다. 이 버퍼에 값이 저장되고 차례차례 파일로 출력을 하는 상황에서 컴퓨터의 전원이 꺼질 경우 출력되다가 처리가 되지 않은 문자들은 저장되지 않는다. 그러나 fclose 함수를 통해 파일을 닫아주면 출력 버퍼에 저장되어 있던 데이터가 파일로 이동하여 출력 버퍼가 비워지고 저장 또한 일어난다. 즉 파일을 모두 사용할 경우에는 바로 fclose 함수를 호출해줘야만 한다.

그리고 fflush 함수의 특성은 그대로 적용된다.

1.     출력 버퍼를 비움 -> 출력 버퍼에 저장된 데이터를 목적지로 전송

2.     입력 버퍼를 비움 -> 입력 버퍼에 저장된 데이터를 소멸 (버퍼의 내용을 읽으면 소멸)

3.     fflush 함수는 출력 버퍼를 비우는 함수

4.     fflush 함수는 입력 버퍼를 대상으로 호출하더라도 결과가 보장되지 않는다.

출력 버퍼를 비우는 것은 fflush 함수를 호출하면 되지만 입력 버퍼를 비우는 것은 함수가 필요하지 않는다. 왜냐하면 파일에 저장된 데이터는 콘솔 입력과는 다르게 언제든 데이터를 읽을 수 있고, 파일 대상의 입력 버퍼를 비워야만 하는 상황이라는 것은 특별히 존재하지 않기 때문이다.

 

  • 파일의 개방 모드(fopen 함수의 두 번째 인자에 대한 고찰)

스트림을 구분하는 기준

1.     읽기 위한 스트림/쓰기 위한 스트림

+는 읽기와 쓰기가 모두 가능한 스트림의 형성을 이야기하고 aappend로 덧붙임을 의미한다. 읽기와 쓰기가 모두 가능한 모드의 경우 메모리 버퍼를 비워줘야 하고 잘못된 사용으로 이어질 수 있다는 점을 주의.

2.     텍스트 데이터를 위한 스트림/바이너리 데이터를 위한 스트림

사람이 인식할 수 있는 문자를 담고 있는 파일을 텍스트 파일이라고 하고, 컴퓨터가 인식할 수 있는 데이터를 담고 있는 파일을 바이너리 파일이라 한다. 이러한 사실과 관련해서 신경 써야하는 부분은 문장의 끝을 의미하는 개행의 표현 방식이다.

‘\n’, 개행을 나타내는 문자이지만 이것은 C언어에서만 나타낸 약속이다. MS-DOS\r\n, Mac\r, Unix 환경에서는 \n이 개행이다. 그렇다면 위의 시스템들 간에 파일을 공유할 때에는 각 시스템마다 개행을 나타내는 방식으로 변환이 필요한데 파일을 텍스트 모드로 개방하게 되면 자동으로 변환이 진행된다. 반대로 바이너리 모드로 파일을 개방하게 되면 아무런 변환이 이루어지지 않는다. 텍스트 모드로 파일을 여는 경우는 사람이 읽도록 출력이 완벽하게 되어야 하기 때문에 각 시스템에 맞도록 변환이 진행되지만, 바이너리 모드로 파일을 여는 경우는 기계가 이해해야 하기 때문에 하나의 손상도 일어나선 안된다. 변환을 통한 손상 또한 일어나서는 안된다. 그렇기 때문에 바이너리 모드는 변환을 일으키지 않는다.

앞서 두 기준을 취합하여 fopen 함수의 두 번째 인자, 스트림 형성을 완성한다.

  • 텍스트 기반의 입/출력 함수

이미 언급한 함수 모두 사용 방법 및 설명이 같다. stream의 인자 부분에 표준 입/출력 디스크립터 대신 파일에 대한 파일 포인터를 넣으면 된다.

 

  • 파일이 종료되었는지 확인 : feof 함수

 

  • 바이너리 기반의 입/출력 함수

바이너리 데이터 입/출력의 경우 텍스트 데이터 입/출력의 경우보다 단순하기 때문에 다양한 함수를 제공하지 않는다.

바이너리 데이터를 입력하는 함수

fread 함수는 size 크기의 데이터 count 개를 stream으로부터 읽어 들여서 buffer에 저장하라는 의미이다.

바이너리 데이터를 출력하는 함수

fwrite 함수는 buffer에 있는 size 크기의 데이터 count stream에 저장하라는 의미이다.

 

  • 서식에 따른 데이터 입/출력: fprintf, fscanf

사용 방법은 첫 번째 인자에 stream을 넣어 파일을 대상으로 하는 것을 제외하고는 printf/scanf랑 완전히 똑같다.

  • 임의 접근을 위한 파일 위치 지시자의 이동

파일 위치 지시자란? FILE 구조체의 멤버 중에는 파일의 위치 정보를 저장하고 있는 멤버가 존재한다. 이 멤버의 값은 각종 읽기 및 쓰기 함수들이 호출될 때마다 참조 및 갱신이 된다. 이 멤버에 저장된 위치 정보의 갱신을 통해서 데이터를 읽고 쓸 위치 정보가 유지되는 것이다. 이 멤버를 파일 위치 지시자라 부른다.

그런데 파일을 읽을 때 항상 앞부터 읽는 것이 아닌, 중간부터 혹은 뒤부터 읽을 필요가 종종 생기는데 이 때 이 파일 위치 지시자 멤버를 조작하여 읽을 위치를 지정하는 것이다.

위 함수는 stream으로 전달된 파일 위치 지시자를 wherefrom에서부터 offset 바이트만큼 이동을 의미한다. offset은 음수/양수 값 모두 가능하며, 다음은 wherefrom에 전달되는 상수와 그 의미를 정리한 것이다.

다음은 위의 요소들을 종합하여 사용한 예를 나타낸다.

728x90
반응형

'컴퓨터 언어 정리 > C 언어' 카테고리의 다른 글

19 매크로 선행 처리기  (0) 2020.10.17
18 동적 메모리 할당  (0) 2020.10.15
16 구조체  (0) 2020.10.12
15 문자와 문자열  (0) 2020.09.29
14 함수 포인터와 void 포인터  (0) 2020.09.23

+ Recent posts