파일은 정보가 저장되는 메모리의 일부분이다. 일반적으로 파일은 플로피 디스크, 하드 디스크 또는 자기 디스크와 같은 영구 메모리에 보관된다. 컴퓨터 시스템에서 파일의 존재가 얼마나 중요한지는 이미알고 있을 것이다. 예를 들어, C로 작성한 프로그램과 그 프로그램을 컴파일 시켜 주는 프로그램은 모두 파일로 보관된다. 따라서 몇몇 프로그램들은 파일에 접근할 수 있어야 한다. 예를 들어, echo.c라는 파일에 저장된 프로그램을 컴파일하는 경우 컴파일러는 echo.c파일을 열고 그 안에 있는 내용을 읽는다. 그리고 컴파일이 끝나면 해당 파일을 다시 닫고 워드 프로세서와 같은 프로그램은 파일 열기, 읽기 및 닫기 뿐만 아니라 파일 쓰기도 한다.
강력하면서도 융통성이 뛰어난 C는 파일 열기, 읽기, 쓰기 및 닫기를 위한 라이브러리 함수들을 많이 제공하고 있다. 일차적으로 C는 해당 시스템에서 제공하는 기본 파일 도구들을 이용해 파일을 처리한다. 이것을 저급 입츨력이라고 한다. 하지만 시스템마다 차이가 많아 모든 시스템에 적용되는 저급 입출력에 대한 표준을 정하기가 어렵기 때문에 ANSI C는 이것을 시도 하지 않는다. 또한 C는 표준 입출력 패키지라는 고급 파일 처리함수들을 제공한다. 이를 위해 파일 처리를 위한 표준 모델과 표준 입출력 함수셋이 제공된다. 고급 입출력에서는 특정 C 시스템 구현이 이러한 시스템 간의 차이점을 처리해 주기 때문에 프로그래머는 공통된 인터페이스만 처리하면된다.
그렇다면 시스템 간의 차이점이란 무엇을 말하는 것일까? 우선, 각 시스템은 서로 다른 파일 저장 방식을 가지고 있다. 어떤 시스템에서는 파일 내용과 파일 정보를 서로 다른 장소에 보관시키는가 하면, 또 어떤시스템에서는 단일 파일 안에 이를 모두 보관한다. 텍스트를 다루는 방법에 있어서도 한 라인의 끝을 표시하기 위해 개행 문자만을 사용하는 시스템이 있는 반면 캐리지 리턴 문자와 라인피드 문자를 혼합해서 사용하는 시스템도 있다. 파일 크기의 측정도 바이트 단위, 블록 단위 등 그 방법이 다양하다
표준 입출력 패키지를 사용하면 이러한 차이점에 영향을 받지 않는다. 따라서 개행 문자를 검사하기 위해 if(ch = '\n')을 사용할 수 있다. 만약 해당 시스템이 캐리지 리턴/라인피드 조합을 사용하는 경우 입출력 함수는 해당 시스템에 맞게 자동 변환을 해준다.
개념적으로 볼때 C프로그램은 하나의 파일을 직접 다 처리하는 것이 아니라 스트림 단위로 처리한다. 하나의 스트림은 데이터의 실제 매핑 시의 이상적인 데이터의 이상적인 양을 말한다. 따라서 특성이 서로 다른 다양한 종류의 입력이 있어도 단일한 특성을 가진 스트림으로 대표될 수 있다. 결국 파일을 여는 고정은 하나의 파일에 하나의 스트림을 연상시키는 과정이 된다. 즉, 파일의 읽기와 쓰기는 스트림을 통해 발생한다.
C의 입출력 장치 처리 방법은 저장 장치의 일반적인 파일 처리 방법과 같다고 생각하면된다. 특별히, 키보드나 모니터 장치 등을 C프로그램이 자동으로 여는 파일로 생각하면 된다. 이때 키보드 입력은 stdin이라는 스트림으로 대표되고 모니터로 출력되는 것은 stdout라는 스트림으로 대표된다. getchar(), putchar(), printf(), scanf()함수 들은 모두 이 두 종류의 스트림을 처리해 주는 표준 입출력 패키지에 속한다.
지금 까지 설명한 바에 따르면 키보드 입력 기법을 파일 처리에서도 마찬가지로 사용할 수 있다. 예를 들어 파일을 읽는 프로그램은 파일 종료를 알 수 있는 방법이 필요하다 그래야 언제 읽기를 중단할지 판단할 수 있기 때문이다. C입력 함수들은 EOF(End of File)를 자동으로 검색해 주는 기능을 모두 내장하고 있다. 키보드 입력이 파일처럼 취급되기 때문에 EOF검색 기능을 키보드 입력 종료를 위해 사용할 수 있다.
EOF
컴퓨터 운영체제는 파일이 어디서 시작하고 끝나는지를 알아야 한다. EOF를 아는 방법하나는 그 끝에 특수 문자를 위치시키는것이다. 이 방법은 CP/M, IBM-DOS 및 MS-DOS파일 등에서 사용되었다. 하지만 오늘날 이 시스템에서는 EOF를 표시하기 위해 Ctrl+Z문자를 사용하고 있다. 한때 이방법이 유일한 방법이었지만 현재는 파일 크기를 추적하는 방법등, 새로운 방법들이 사용되고 있다. 그래서 최근에 작성된 텍스트파일안에는 Ctrl+Z문자가 숨겨져 있을 수도 있고 없을 수도 있다. 하지만 만약 있다면 운영체제는 이것을 EOF표시고 간주할것이다.
ex)Ishphat the robot slid open the hatch and shouted his challenge.\n^Z
다음은 파일 크기에 대한 정보를 운영체제가 저장하는 방법이다. 3000바이트의 크기를 가진 파일에 대해서 프로그램이 3000바이트까지 읽었다면 그 끝에 다다른 것을 의미한다. MS-DOS와 비슷한 운영체제에서는 2진파일에 대해서 이방법을 사용한다. 왜냐하면 2진파일에서는 Ctrl+Z를 포함한 모든 문자들을 파일내용에 포함 시킬 수 있기 때문이다. DOS의 초신 버전들에서는 텍스트 파일에 대해서도 이방법을 사용하기시작했다 한편 UNIX는 모든 파일애 대해 이 방법을 적용한다.
C는 파일 끝에 다다랐을 때 getchar()함수가 특별한 값을 반환하도록 함으로써 앞에서 소개한 다양한 방법들을 처리한다. 따라서 각 운영체제가 파일 끝을 검새하는 방법과는 상관없으며 이렇게 반환되는 값을 EOF라고 한다. 따라서 파일 끝을 검색했을 때getchar()함수의 리턴값이 EOF가 되며 scanf()함수도 파일 끝을 검색했을때 EOF값을 반환한다. 일반적으로 EOF는 stdio.h 헤더 파일에 다음과 같이 정의 되어 있다.
#define EOF (-1)
그렇다면 왜 -1 값으로 정의 했는가? 보통 getchar()함수는 -0에서 127 사이의 값을 되돌린다. 왜냐하면 이것이 표준 문자셋에 들어있는 문자들에 대한 코드값들이기 때문이다. 그리고 해당 시스템이 확장문자셋을 인식하는 경우에는 0~255사이의 값이 반환된다. 이렇게 -1값이 어느 문자의 코드값도 되지 않기 때문에 파일 끝을 표시하는데 사용될 수 있다.
EOF의 값을 -1이 아닌 다른 값을 갖도록 정의하는 시스템도 있다. 하지만 이 경우에도 일반적인 입력 문자에 발생된 리턴값과는 항상 다르다. 프로그램에 stdio.h 헤더 파일과 EOF기호를 사용하면 어떤 값이 정의에 사용되었는지에 대해서 걱정하지 않아도 된다. 여기서 기억해야 할 것은 EOF가 파일 안에서 실제로 찾아지는 기호가 아니라 파일이 어디서 끝나는지를 알리는 신호값을 대표한다는 것이다.
그렇다면 프로그램 내에서 EOF를 어떻게 사용하는 것일까? 이것은 getchar()의 리턴값과 EOF를 비교하는 방법으로 사용할 수 있다. 여기서 서로 값이 다르면EOF가 아직 나오지 않았다는 것인데, 구체적으로 다음과 같이 표현식을 만들어 사용할 수 있다.
while((ch = getchar()) != EOF)
만약 파일이 아닌 키보드로 입력 받는 경우는 어떠할까? 대부분의 시스템에서는 파일 끝 조건과 키보드 입력 상황을 일치시키는 방법을 가지고 있다. 따라서 다음 예제와 같이 입력을 읽고 에코시켜 주는 프로그램을 작성할 수 있다.
#include <stdio.h>
int main(void)
{
int ch;
while((ch = getchar()) != EOF)
putchar(ch);
retur 0;
}
이 프로그램과 관련해서 다음 사항을 주의 하도록한다.
- EOF는 stdio.h파일 안에 정의 되어 있기 때문에 정의에 신경 쓰지 않아도 된다.
- stdio.h 헤더 파일 안에 있는 #define 정의로 해서 EOF기호를 사용할 수 있기 때문에 EOF의 값이 얼마인지에 대해서는 걱정하지 않아도 된다. 따라서 EOF를 위해 특정 값을 직접 사용할 필요가 없다.
- ch변수는char형에서 int형으로 자동 변환된다. 그 이유는 char변수가 0 에서 255 사이의 unsigned정수형 값을 가질 수도 있지만 EOF가 -1값을 갖기 때문이다.
이 값은 unsigned char 변수가 갖기에는 불가능한 값이지만 int형을 이 값을 가질 수 있다. 그리고 getchar()는 원래 int형이기 때문에 EOF문자를 읽을 수 있다. 만약 singed char형을 사용하는 시스템이라면 ch를 char형으로 선언해도 문제가 없을 수도 있다. 하지만 이식성을 높이기 위해 일반적인 형식을 사용하는 것이 좋다.
- ch 변수가 정수형이라고해서 putchar()가 정수형을 출력시키는 것은 아니다. 여전히 해당 문자를 출력시켜 준다.
- 이 프로그램을 키보드 입력에 사용할 때에는 EOF 문자를 입력시클 수 있는 방법이 필요하다. 이때 E,O,F식으로 문자를 입력하거나 또는 리턴값인 -1을 입력할 수는 없다.(-1은 하이픈과 1 두문자로 인식된다.)대신 이를 위해 각 시스템이 요구하는 사항을 알아보아야한다. 예를 들어 대부분의 UNIX시스템에서는 새로운 라인의 시작점에서 Ctrl+D를 입력하면 EOF신호가 전송된다. 그리고 많은 마이크로 컴퓨터 시스템은 라인 어느곳에서나 Ctrl+D를 입력하면 EOF로 간주한다.
아래는 위 소스를 컴파일 하여 실행시킨 예이다.
She walks in beauty, like the night
She walks in beauty, like the night
Of cloudless climes and starry skies...
Of cloudless climes and starry skies...
[Ctrl + D]
엔터키를 누를 때마다 버퍼에 저장되어 있던 문자들이 처리되어 한 라인에 출력된다.
UNIX시스템에서는 파일 끝상황이 일어날때까지 계속된다. 그리고 PC에서는 Ctrl+Z가 눌러질 때 까지 계속된다.
잠깐 위 소스의 발전 가능성에 대해서 살펴보자.
이 프로그램은 입력한 것은 무엇이든 모니터로 출력시켜 준다. 만약 파일을 입력하면 어떻게 될까? 그러면 이 프로그램은 파일의 내용을 모니터 스크린으로 출력시켜준다. 이때 파일의 EOF신호를 찾으면 출력을 끝낸다. 여기서 이 프로그램의 출력을 파일로 전송시키려면 어떻게 할까? 이럴 경우 데이터를 키보드로 부터 입력시킨다음 다음에 위소스가 이 데이터를 파일로 저장시키도록 하면 된다. 그러면 파일의 입력과 출력을 동시에 할 수 있다면 어떻게 될까? 이 경우에는 소스로 하여금 파일을 복사하도록 만들 수 있다 이 프로그램은 파일의 내용을 읽고 새로운 파일을 생성하며 파일을 복사하는 잠재성을 가지고 있다. 이러한 기능들은 조그만 프로그램에 있어서는 놀라운 기능이라고 할 수 있다. 여기서 중요한점은 입력과 출력의 흐름을 어떻게 통제 하는가이다.
방향 재설정
입출력엔ㄴ 함수, 데이터 및 주변 장치가 필요하다. 예를 들어 상기 소스를 생각해 보면 이 프로그램은 입력함수인 getchar()를 사용한다. 그리고 입력장치로는 키보드를 사용하고 입력 데이터 스트림은 개별 문자들로 구성된다. 이때 똑같은 입력 함수와 데이터를 사용하면서 프로그램이 데이터를 찾는 장소를 다르게 하려면 어떻게 할까? 이 질문은 프로그램의 입력이 어디에 있는지 어떻게 아는 것일까? ㄹ는 의구심을 불러일으킨다.
기본적으로 푲ㄴ 입출력 패키지를 사용하는 C프로그램은 입력 소스로 표준 입력을 찾는 다. 이것은 stdin으로 정의된 스트림인데, 여기에 컴퓨터에 데이터가 입력되는 방법이 정의되어있다. 이 정의는 자기 테이프와 같은 오래된 장치나 펀치 또는 전신기나 키보드 아니면 아직 개발 단ㄱ에 있는 음성 입력 장치를 지정할 지도 모른다. 하지만 현대 컴퓨터는 신축적이어서 사용자가 컴퓨터에 명령을 내려 다른 곳에서 입력을 찾으라고 설정할 수도 있다. 즉, 특별히 프로그램으로 하여금 키보드가 아닌 파일로 부터 입력을 찾으라고 요구할 수 있는 것이다.
프로그램을 ㅏ일과 작동하도록 하는 방법은 두가지가 있다. 첫 번째 방법은 파일 열기, 다디, 읽기 및 쓰기가있다. 두번째 방법은 키보드와 모니터로 입력과 출력이 작동되는 프로그램에 대해서, 다른 채널을 통해서 이루어지도록 방향 재설정을 하는 것이다. 일례로 파일을 통해 입ㅊㄹ력을 하도록 재설정할 수 있다. 즉, stdin스트림을 파일로 재지정하는 것이다. getchar() 프로그램은 데이터를 스트림으로 부터 계속 받지만 그 스트림이 어딩서 오는지에는 관심이 없다. 이러한 방향 재설정 방법은 첫 번째 방법보다 몇가지 면에서 제한적이지만 사용하기가 간편하면서도 일반적인 파일 처리 기술에 친숙해 질수 있다는 장정이있다. 방향 재설정 방법의 주요 문제점은 이 방법이 C가 아닌 운영체제와 관련되어 있다는 것이다. 하지만 UNIX, LINUX 및 MS-DOS 드으이 다양한 C환경에서 방향 재설정 기능을 제공하고 있으며 이러한 기능이 제공되지 않는 시스템에서도 C구현으로 시뮬레이션 할 수 있다.
방향재설정의 예(unix, linux, ms-dos)
$ ls -al > text
ls -al의 결과내용이 text라는 파일명으로 저장이 되는것이다.
즉, ls -al의 결과가 입력이 되므로 입력방향의 재설정이 되는것이고 출력은 화면이 아닌 text라는 파일로 되는것이다.
$ ./echo_eof < text /* echo_eof는 위 소스 컴파일한 실행파일명임. */
위와같이 실행했을시 text의 내용이 echo_eof의 표준입력값이 된다.
그러면 프로그램의 에코기능상 입력된 값에서 EOF를 만날때 까지 표준출력인 화면으로 text의 내용을 화면으로 출력해 줄것이다.
그리고 UNIX, Linux 및 DOS는 >>연산자도 제공한다. 이 연사나를 사용하면 새로운 데이터를 이미 존재하는 파일의 데이터 뒤에 붙여 줄 수 있다. 그리고 | 연삱를 사용하면 한 프로그램의 출력을 두번째 프로그램의 입력에 붙여 줄 수 있다.
Do you understand?