FILE
FILE
파일 입출력을 하려면 반드시 거치는 타입이 있어요. 그게 바로 FILE이에요. C에서 파일 스트림을 다룰 때 이 포인터를 함수 인자로 계속 넘기면서 작업해요.
출처: cppreference
본문
정의 (Definition)
typedef /* unspecified */ FILE;
FILE 객체 하나가 하나의 C 스트림을 나타내요. FILE이 완전한(complete) 객체 타입인지는 표준이 정하지 않았어요. 그래서 유효한 FILE을 복사할 수도 있겠지만, 그 복사본의 포인터를 입출력 함수 인자로 쓰면 지정되지 않은(unspecified) 동작이 돼요. 쉽게 말해 FILE은 개념적으로 복사할 수 없는 타입이라고 보면 돼요.
입출력 스트림은 형식이 없는(unformatted) 입출력과 형식이 있는(formatted) 입출력 양쪽에 모두 쓸 수 있어요. 또 입력·출력을 다루는 함수는 로케일에 민감해서, 필요하면 와이드/멀티바이트 변환이 알아서 이뤄지기도 해요.
스트림 상태 (Stream state)
장치에 접근하는 데 필요한 시스템별 정보(예: POSIX 파일 디스크립터) 말고도, FILE 객체는 직접 또는 간접적으로 다음을 담고 있어요.
- (C95) 문자 폭: 설정 안 됨, 좁은 문자, 와이드 문자
- (C95) 멀티바이트와 와이드 문자 사이 변환을 위한 파싱 상태(
mbstate_t타입의 객체) - 버퍼링 상태: 버퍼링 안 함, 줄 버퍼링, 완전 버퍼링
- 버퍼. 외부에서 만든 사용자 버퍼로 교체될 수도 있어요
- 입출력 모드: 입력, 출력, 또는 갱신(update, 입력·출력 모두)
- 이진/텍스트 모드 표시
- 파일 끝(end-of-file) 상태 표시기
- 오류 상태 표시기
- 파일 위치 표시기.
fpos_t타입의 객체로 접근하며, 와이드 스트림의 경우 파싱 상태까지 포함해요 - (C11) 재진입 가능한 잠금(reentrant lock). 여러 스레드가 동시에 같은 스트림을 읽거나 쓰거나 위치를 옮기거나 조회할 때 데이터 경쟁을 막아줘요
갱신(update) 입출력 모드인 스트림에서는 두 가지 규칙을 지켜야 해요.
- 출력 연산에서 입력 연산으로 전환하려면, 그 사이에
fflush나 파일 위치 함수(fseek,fsetpos,rewind중 하나) 호출이 있어야 해요 - 입력 연산에서 출력 연산으로 전환할 때, 입력 연산이 파일 끝(EOF)을 만나지 않았다면 그 사이에 파일 위치 함수 호출이 있어야 해요
좁은 문자 / 와이드 문자 지향 (Narrow and wide orientation)
갓 연 스트림은 아직 지향(orientation)이 없어요. fwide나 어떤 입출력 함수든 첫 번째 호출이 지향을 결정해요. 와이드 입출력 함수를 호출하면 스트림이 와이드 지향이 되고, 좁은 입출력 함수를 호출하면 좁은 지향이 돼요. 한 번 정해지면 지향은 freopen으로만 바꿀 수 있어요. 와이드 지향 스트림에서는 좁은 입출력 함수를, 좁은 지향 스트림에서는 와이드 입출력 함수를 호출할 수 없어요.
와이드 입출력 함수는 mbrtowc나 wcrtomb를 호출하는 것처럼 와이드 문자와 멀티바이트 문자를 변환하는데, 스트림이 갖고 있는 변환 상태를 그대로 사용해요. 프로그램 안에서 유효한 멀티바이트 문자열과 달리, 파일 안의 멀티바이트 문자 시퀀스는 널 문자를 중간에 포함할 수 있고, 초기 시프트 상태에서 시작하거나 끝날 필요도 없어요.
와이드 지향 스트림의 변환 상태는 스트림의 지향이 정해지는 시점에 설치된 C 로케일에 의해 확정돼요.
이진 모드와 텍스트 모드 (Binary and text modes)
텍스트 스트림은 줄로 묶을 수 있는 문자들의 순서 있는 나열이에요. 줄은 0개 이상의 문자에 종료 '\n'(뉴라인) 문자를 더한 것으로 나눌 수 있어요. 마지막 줄에 종료 '\n'이 필요한지는 구현에서 정의해요. 여기에 더해, 운영체제에서 텍스트를 표현하는 관례에 맞추려고 입력·출력 시 문자가 추가되거나, 바뀌거나, 삭제될 수 있어요. 특히 Windows의 C 스트림은 출력할 때 '\n'을 '\r\n'으로 바꾸고, 입력할 때는 '\r\n'을 '\n'으로 바꿔요.
텍스트 스트림에서 읽은 데이터가 아까 그 스트림에 써 넣었던 데이터와 완전히 같다고 보장되려면 다음 조건이 모두 참이어야 해요.
- 데이터가 인쇄 가능한 문자와/또는 제어 문자
'\t','\n'만으로 이뤄져 있어야 해요(특히 Windows에서는'\x1A'문자 하나가 입력을 끝내요) - 어떤
'\n'문자 바로 앞에 공백 문자가 없어야 해요(그런 공백 문자는 나중에 그 출력을 입력으로 읽을 때 사라질 수 있어요) - 마지막 문자가
'\n'이어야 해요
이진 스트림은 내부 데이터를 투명하게 기록할 수 있는 문자들의 순서 있는 나열이에요. 이진 스트림에서 읽은 데이터는 예전에 그 스트림에 써 넣었던 데이터와 항상 같아요. 단, 구현이 스트림 끝에 개수가 정해지지 않은 널 문자를 붙이는 건 허용해요. 와이드 이진 스트림은 초기 시프트 상태로 끝날 필요가 없어요.
참고 (Notes)
POSIX는 스트림의 지향이 와이드로 정해지는 순간, 현재 설치된 C 로케일의 LC_CTYPE 요소를 FILE 객체 안에 저장할 것을 명시적으로 요구해요. 그리고 지향이 바뀔 때까지는 이후 setlocale 호출과 무관하게 이 LC_CTYPE 요소를 그 스트림의 모든 향후 입출력에 사용해야 한다고 요구해요.
각 텍스트 줄은 본질적으로 사람이 읽을 수 있는 데이터로 구성되는 게 의도예요. POSIX 구현은 텍스트 스트림과 이진 스트림을 구분하지 않아요('\n'이나 다른 문자에 특별한 변환을 적용하지 않아요).
참고 문헌 (References)
- C23 표준 (ISO/IEC 9899:2024): 7.24 Input/output
<stdio.h>(p: 321-355), 7.31 Extended multibyte and wide character utilities<wchar.h>(p: 415-450) - C17 표준 (ISO/IEC 9899:2018): 7.21 Input/output
<stdio.h>(p: 217-247), 7.29 Extended multibyte and wide character utilities<wchar.h>(p: 295-325) - C11 표준 (ISO/IEC 9899:2011): 7.21 Input/output
<stdio.h>(p: 296-339), 7.29 Extended multibyte and wide character utilities<wchar.h>(p: 402-446) - C99 표준 (ISO/IEC 9899:1999): 7.19 Input/output
<stdio.h>(p: 262-305), 7.24 Extended multibyte and wide character utilities<wchar.h>(p: 348-392) - C89/C90 표준 (ISO/IEC 9899:1990): 4.9 INPUT/OUTPUT
<stdio.h>
더 알아보기
FILE*타입의 표준 스트림인stdin,stdout,stderr와 함께 보면 프로그램 입출력의 기본 골격이 보여요.- 실제로 파일을 여는 fopen과 닫는 fclose 문서를 이어서 보면 스트림의 생성과 소멸을 이해할 수 있어요.
- cppreference 원문에서
FILE타입의 전체 설명을 확인할 수 있어요.