HOWTO · C

C에서 파일 리디렉션 사용

이 기사에서는 C에서 파일 리디렉션을 사용하는 방법을 보여줍니다.

이 페이지의 내용

명령줄에서 C 프로그램의 입력이나 출력을 리디렉션하려면 셸 연산자 <, >, >>를 사용합니다. 셸은 프로그램의 표준 스트림을 파일에 연결합니다. C 코드에서는 fopen으로 이름이 지정된 파일을 열거나 freopen으로 표준 스트림을 바꿀 수 있습니다. 모두 데이터를 다루는 방법이지만 서로 다른 작업입니다.

C에서 파일 리디렉션 이해하기

C 프로세스는 세 가지 표준 스트림으로 시작합니다. stdin은 입력, stdout은 일반 출력, stderr는 진단 메시지에 사용됩니다. 셸은 보통 이 스트림을 터미널에 연결합니다. 프로그램을 시작할 때 리디렉션하면 이 연결이 바뀌므로, 프로그램은 스트림이 터미널로 향하는지 파일로 향하는지 알 필요 없이 fgets, printf 또는 fprintf(stderr, ...)를 계속 호출할 수 있습니다.

반면 fopen은 코드가 직접 읽거나 쓸 수 있는 별도의 FILE *를 반환합니다. 프로그램이 여러 파일 중 하나를 선택하거나, 여러 파일을 동시에 다루거나, 파일의 수명을 제어해야 한다면 fopen을 사용하세요. 실행할 때 입력이나 출력 대상을 호출자가 선택하게 하려면 셸 리디렉션을 사용하세요.

> 및 »로 프로그램 출력 리디렉션하기

다음 간단한 프로그램은 stdout에 한 줄을 씁니다.

이 예제를 실행해 보세요.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#include <stdio.h>

int main(void) {
    puts("Report complete");
    return 0;
}

프로그램의 한 줄은 stdout으로 전달됩니다. 이 예제에는 실행 결과 기록이 함께 제공되지 않습니다. 프로그램을 report로 컴파일한 뒤 ./report > report.txt를 실행하면 report.txt를 만들거나 기존 내용을 덮어씁니다. 파일 끝에 추가하려면 ./report >> report.txt를 사용하세요. > 연산자는 프로그램이 시작되기 전에 기존 대상 파일의 내용을 비우고, >>는 파일 끝에 씁니다. 셸 문법과 사용할 수 있는 컴파일러 명령은 플랫폼마다 다를 수 있습니다.

리디렉션 대상 파일을 여는 일은 셸이 처리합니다. 파일을 열지 못하면 셸이 문제를 알리고 보통 프로그램을 시작하지 않습니다. 프로그램이 대상 파일을 직접 열지 않았으므로 이를 fopen 실패로 감지할 수는 없습니다. 진단 메시지를 별도 파일에 저장하려면 ./report > report.txt 2> errors.txt를 사용하세요. 2>는 stderr에 쓰일 때 관례적으로 사용하는 파일 디스크립터를 리디렉션합니다.

코드 블록은 정본과 바이트 단위로 동일하게 유지되므로, 블록 안의 문자열이나 화면에 표시되는 문구는 영어 그대로입니다.

<로 프로그램 입력 리디렉션하기

호출자가 입력 소스를 선택해야 한다면 stdin에서 읽으세요. 다음 프로그램은 리디렉션된 파일의 끝에 도달할 때까지 줄을 읽습니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#include <stdio.h>

int main(void) {
    char line[128];

    while (fgets(line, sizeof line, stdin) != NULL) {
        if (fputs(line, stdout) == EOF) {
            perror("writing stdout");
            return 1;
        }
    }

    if (ferror(stdin)) {
        perror("reading stdin");
        return 1;
    }
    return 0;
}

프로그램을 copy-input으로 컴파일한 다음 ./copy-input < input.txt를 실행하세요. fgets는 버퍼 용량보다 최대 한 문자 적게 읽고, 끝에 널 문자를 추가하며, 읽은 내용에 줄바꿈 문자가 있으면 이를 유지합니다. 따라서 긴 한 줄은 여러 조각으로 나뉘어 반환될 수 있습니다. 루프가 끝난 뒤 ferror를 확인하면 읽기 오류와 정상적인 파일 끝을 구분할 수 있습니다. 예제의 동작은 표시된 C 스트림 연산에 따르며, 여기에는 관찰된 출력 결과가 제공되지 않습니다.

코드 블록은 정본과 바이트 단위로 동일하게 유지되므로, 블록 안의 문자열이나 화면에 표시되는 문구는 영어 그대로입니다.

C 프로그램 내부에서 표준 스트림 리디렉션하기

프로그램이 직접 출력 대상을 선택해야 한다면 freopen으로 표준 스트림을 다시 열 수 있습니다. freopen에는 경로, "w" 또는 "a"와 같은 모드, 다시 열 스트림을 전달합니다. 예를 들어 freopen("run.log", "a", stdout)은 이후의 표준 출력을 run.log의 끝에 추가하도록 설정합니다. 쓰기 전에 반환값을 확인하세요. 스트림을 다시 열지 못하면 NULL을 반환합니다. 이 작업은 프로세스의 스트림 자체를 바꿉니다. fopen은 stdout을 그대로 두고 별도의 스트림을 반환합니다.

리디렉션이 프로그램 자체의 동작에 포함되어야 할 때 이 방법을 사용하세요. 호출자가 대상을 정해야 한다면 stdout을 바꾸지 말고 > 또는 >>를 사용하세요. freopen은 표준 C 함수지만, 실패 후 스트림을 다시 여는 세부 동작은 구현에 따라 달라질 수 있습니다. NULL 반환을 처리하고 리디렉션이 성공한 것처럼 계속 진행하지 마세요.

오류 처리와 적절한 방법 선택

실패할 수 있는 모든 작업의 결과를 확인하세요. 반환된 스트림을 사용하기 전에 fopen과 freopen을 검사하고, fprintf나 fputs 같은 출력 함수가 실패했는지도 확인하세요. 버퍼에 남은 출력이 파일에 기록되었는지가 중요하다면 fclose의 결과도 확인하세요. 입력 루프에서 fgets가 NULL을 반환하는 이유는 파일 끝이거나 읽기 오류일 수 있습니다. ferror를 확인해 둘을 구분하세요. perror는 현재 errno와 관련된 실패에 대해 운영 체제의 오류 메시지를 표시할 수 있습니다.

파일에 보고된 바이트 길이를 기준으로 줄 버퍼의 크기를 정하지 마세요. 입력은 파이프나 터미널에서 들어올 수 있고, 한 줄이 선택한 버퍼보다 길 수도 있습니다. 고정 크기 버퍼와 반복적인 fgets 호출은 데이터를 여러 조각으로 처리해도 될 때 적합합니다. 길이에 제한이 없는 한 줄을 처리하려면 조각을 읽으면서 버퍼를 늘리고 메모리 할당 및 산술 연산의 한계를 확인하세요. 또는 이식성상의 제약을 명시하고 플랫폼별 줄 읽기 기능을 사용할 수 있습니다.

실행의 표준 스트림을 연결할 때는 셸 리디렉션을, C 코드에서 별도 파일을 다룰 때는 fopen을, 프로그램이 표준 스트림 자체를 바꿔야 할 때는 freopen을 선택하세요. 일반 출력을 리디렉션할 때 진단 메시지는 stderr에 유지하고, 작업을 수행한 계층에서 실패를 확인하세요.