18장. 파일 I/O와 디렉터리 컨트롤

유니야·2023년 1월 24일

ANSI에서는 파일 I/O 관련 함수를 정의해두고 있음.
해당 표준 함수를 쓰면 운영체제에 상관없이 파일이 생성되고 파일 관련 연산 등을 할 수 있다.

하드웨어가 하나 있다고 가정. 해당 하드웨어에는 OS가 설치되어 있음.
OS 위에서 프로그램을 동작. 파일을 생성하면 해당 파일이 하드디스크에 저장.
파일이 저장되는 방식은 File System에 의존적.
이 때 파일을 어떻게 저장할 것이냐, 즉 파일 자체를 어떻게 정의하고 구성할 것인지가 중요.
파일은 운영체제의 일부이자 운영체제에서 구현한 독립적인 시스템.
파일 관련 함수를 사용할 때 ANSI 표준 함수를 호출하지만, 실제 파일을 만드는 것은 OS.

ANSI 표준 함수가 구현되기 이전 Windows가 제공하는 System 함수가 먼저 존재.
Windows라는 OS에서 파일을 만드려면 이 System 함수를 사용하면 됨.

  • ANSI 표준 함수와 System 함수의 차이점

ANSI 표준 함수의 내부를 보면 해당 OS의 System 함수를 호출하고 있음. ex) fopen()
다시 말해 ANSI 표준 함수가 실제로 파일을 만드는 역할을 하는건 아니고, System 함수를 감싸고 있는 껍데기.

ANSI 표준 함수는 모든 OS의 공통분모만 함수로 제공함. 만약 사용자가 세밀한 파일 작업을 해야 한다면 각 OS가 제공하는 File System 함수를 사용할 필요가 있다.

💻 파일 관련 함수들

  • 파일 생성
    CreateFile()

  • 입력 및 출력
    ReadFile(), WriteFile()

  • 파일 종료
    CloseHanlde()

파일도 커널이 관리하는 리소스이기 때문에 파일 생성 시 커널 오브젝트가 생성되고, 해당 커널 오브젝트의 핸들이 반환된다.
File Pointer: 파일을 어디까지, 몇 바이트까지 읽었는지에 대한 정보를 저장해서 어디서부터 다시 읽어올지 컨트롤.

  • 파일 정보 얻어오기
    GetFileTime()
    • 만든 날짜, 수정한 날짜, 액세스한 날짜
    • 핸들을 통해 정보 확인
    GetFileAttributes()
    • 읽기 전용, 숨김, 보관
    • 파일 이름을 통해 정보 확인
    GetFileInformationByHandle()
    • 위 두 함수를 통해 얻을 수 있는 정보를 모두 얻을 수 있음
    • 핸들을 통해 정보 확인

파일 정보를 얻을 때 핸들을 통해 얻을지, 이름을 통해 얻을지 두 가지를 고려해야 한다.
일반적으로 새로운 파일을 생성하는게 아닌 이상 핸들을 통해 얻는 함수가 더 유용하다.

💻 파일 포인터의 이동

fseek(): 파일 포인터를 이동하는 함수
32비트 시스템에서는 파일의 최대 크기가 4GB 기준. 파일의 크기가 4GB보다 작으면 32비트로 표현 가능하지만, 이보다 크면 32비트로 표현할 수 없기 때문에 만들 수 없었음.

  • 32비트 시스템에서 파일의 최대 크기가 4GB(2^32 Byte)인 이유

파일의 크기가 4GB일 경우, 파일의 주소가 0 ~ 4GB - 1 Byte 내에 위치할 것.
따라서 파일 주소 4GB에 위치한 데이터를 얻고자 하면 4GB를 표현할 수 있어야 함.
32비트 시스템에서 표현할 수 있는 주소 값의 최대 크기가 4GB이기 때문에 파일의 최대 크기도 4GB.

  • 32비트 기반
    • SetFilePointer(hFile, sizeof(TCHAR)* 4, NULL, FILE_BEGIN);
      • 파일 포인터의 현재 위치를 반환하는 함수.
      • hFile: 옮기고 싶은 파일의 핸들 정보
      • sizeof(TCHAR)* 4: 상위 4바이트
        SetFilePointer() 함수는 64비트 기반으로 디자인된 함수. 파일 크기가 2^64 - 3 Byte일 경우, 0 ~ 2^32 - 1번지 까지 표현 가능.
      • NULL: 하위 4바이트
        파일 크기가 2^64 - 3 Byte일 경우, 32비트 시스템에서는 표현 불가능한 범위이기 때문에 해당 인자는 NULL. 64비트 시스템에서는 2^32 ~ 2^64 - 3 Byte.
      • FILE_BEGIN: 파일의 일부 데이터를 옮길 때, 어디서부터 옮길지 기준점
    • 파일 최대 크기: 4GB - 2
      • 2^32 Byte = 4GB로 표현할 수 있는 주소 범위는 0 ~ 2^32 - 1 Byte. 이 때 가장 마지막 번지가 파일의 크기.
      • 파일 관련 I/O 함수 호출했을 때 오류가 발생한다면 INVALID_SET_FILE_POINTER(0xFFFFFFFF), 즉 -1을 반환함. 이는 비트 단위로 표현한다면 4GB - 1 Byte. 만약 파일의 크기를 4GB - 1 Byte까지 허용한다면 4GB - 1이 반환되었을 때 파일의 실제 크기를 의미하는지, 오류 코드를 의미하는지 알 수 없음. 따라서 파일의 최대 크기를 4GB - 2 Byte로 제한.
    • 4GB - 1 == INVALID_SET_FILE_POINTER(0xFFFFFFFF)
  • 64비트 기반
    • SetFilePointer(hFile, IDistanceLow, &IDistanceHigh, FILE_BEGIN);
      • IDistanceLow: 하위 4바이트 반환
      • IDistanceHigh: 상위 4바이트 반환
    • 4GB - 1의 검사
      • 64비트 시스템에서도 4GB - 1은 오류 코드의 의미를 담고 있음. 따라서 아래와 같이 반환 값이 실제 파일의 크기인지, 오류 코드인지 검사해야 함.
dwPtrLow = SetFilePointer(...)

if ((INVALIDE_SET_FILE_POINTER == dwPtrLow)
&& (NOERROR != GetLastError()))
{ ..진짜 오류.. }

0개의 댓글