`fseek` — 파일 위치 지시자를 옮기는 함수

fseek — 파일 위치 지시자를 옮기는 함수

파일을 읽다 보면 "처음부터 다시 읽고 싶다"거나 "세 번째 값부터 읽자" 같은 순간이 꼭 생기죠. 그럴 때 스트림의 파일 위치 지시자(file position indicator)를 원하는 곳으로 옮겨 주는 게 fseek예요. 파일의 어디를 기준으로 얼마만큼 이동할지를 지정하면 됩니다.

출처: cppreference — fseek

본문

fseek은 파일 스트림 stream의 파일 위치 지시자를, origin을 기준으로 offset만큼 이동한 값으로 설정해요.

문법

#include <stdio.h>

int fseek( FILE *stream, long offset, int origin );

origin에는 세 가지 매크로를 쓸 수 있어요.

#define SEEK_SET  /* unspecified */
#define SEEK_CUR  /* unspecified */
#define SEEK_END  /* unspecified */

설명

스트림이 이진(binary) 모드로 열려 있을 때는 새 위치가 정확히 offset 바이트만큼 떨어진 자리예요. 기준은 origin이 결정합니다.

  • SEEK_SET — 파일의 시작에서부터 offset 바이트
  • SEEK_CUR — 현재 파일 위치에서부터 offset 바이트
  • SEEK_END — 파일의 끝에서부터 offset 바이트

단, 이진 스트림이 SEEK_END를 반드시 지원해야 하는 건 아니에요. 특히 끝에 추가적인 널 바이트가 출력되는 경우에는 지원하지 않아도 됩니다.

스트림이 텍스트 모드로 열려 있을 때는 이야기가 달라져요. 이 경우 offset으로 허용되는 값은 딱 두 가지입니다.

  • 0 — 어떤 origin과 함께든 사용 가능
  • 같은 파일에 대한 이전 ftell 호출이 돌려준 값 — 단 originSEEK_SET일 때만

스트림이 **와이드 지향(wide-oriented)**이면 텍스트 모드와 이진 모드 양쪽의 제약이 모두 적용돼요. ftell의 결과는 SEEK_SET에 쓸 수 있고, 0 오프셋은 SEEK_SETSEEK_CUR에 허용되지만 SEEK_END에는 안 됩니다.

위치를 옮기는 것 외에도 fseek은 두 가지 부수 효과를 냅니다. ungetc의 효과를 되돌리고, 해당될 때 파일 끝 상태(end-of-file status)를 지워줘요. 읽기나 쓰기 오류가 발생하면 스트림의 오류 지시자(ferror)가 설정되고 파일 위치는 그대로예요.

매개변수

매개변수 설명
stream 수정할 파일 스트림
offset origin을 기준으로 이동할 문자 수
origin offset을 더할 기준 위치. SEEK_SET, SEEK_CUR, SEEK_END 중 하나

반환값

성공하면 0, 실패하면 0이 아닌 값을 돌려줘요.

주의

와이드 스트림에서 끝이 아닌 위치로 이동한 뒤에는, 다음에 어떤 출력 함수를 호출해도 파일의 나머지 부분이 정의되지 않은 상태로 렌더링될 수 있어요. 예를 들어 다른 길이의 멀티바이트 시퀀스를 출력하면서 그렇게 될 수 있는 거죠.

텍스트 스트림에서 유효한 offset0(어느 origin이든)과, 이전 ftell 호출의 반환값(SEEK_SET에만)뿐이라는 점, 다시 한번 짚고 넘어갈게요.

POSIX는 기존 파일 끝을 넘어서는 위치로의 seek를 허용해요. 그 뒤에 출력을 수행하면, 그 빈 공간을 읽으면 0 바이트가 돌아옵니다. 파일시스템이 지원한다면 이게 희소 파일(sparse file)을 만들어 내기도 해요. POSIX에는 fseek이 쓰지 않은 데이터가 있으면 먼저 fflush를 수행해야 하고, 오류 시 -1을 돌려주고 errno를 설정해야 한다는 규칙도 있습니다.

Windows에서는 _fseeki64를 쓰면 2 GiB보다 큰 파일도 다룰 수 있어요.

예시

오류 검사를 곁들인 fseek 예시예요. double 배열을 파일에 쓴 뒤, 세 번째 값을 읽기 위해 위치를 이동합니다.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    // Prepare an array of double values.
    #define SIZE 5
    double A[SIZE] = {1.0, 2.0, 3.0, 4.0, 5.0};

    // Write array to a file.
    FILE *fp = fopen("test.bin", "wb");
    fwrite(A, sizeof(double), SIZE, fp);
    fclose(fp);

    // Read the double values into array B.
    double B[SIZE];
    fp = fopen("test.bin", "rb");

    // Set the file position indicator in front of third double value.
    if (fseek(fp, sizeof(double) * 2L, SEEK_SET) != 0)
    {
        fprintf(stderr, "fseek() failed in file %s at line # %d\n",
                __FILE__, __LINE__ - 2);
        fclose(fp);
        return EXIT_FAILURE;
    }

    int ret_code = fread(B, sizeof(double), 1, fp); // reads one double value
    printf("ret_code == %d\n", ret_code);           // prints the number of values read
    printf("B[0] == %.1f\n", B[0]);                 // prints one value

    fclose(fp);
    return EXIT_SUCCESS;
}

가능한 출력:

ret_code == 1
B[0] == 3.0

sizeof(double) * 2L만큼 SEEK_SET으로 이동했으니 세 번째 값부터 읽는 거죠. fseek의 반환값이 0이 아닌지, 즉 실패했는지를 꼭 확인하고 넘어가는 걸 볼 수 있어요.

더 알아보기

  • fsetpos — 파일 위치 지시자를 파일의 특정 위치로 이동해요.
  • fgetpos — 파일 위치 지시자를 얻어요.
  • ftell — 현재 파일 위치 지시자를 돌려줘요.
  • rewind — 파일 위치 지시자를 파일의 시작으로 옮겨요.
  • cppreference의 fseek 원문에서 표준 항목별 세부 규칙을 더 확인할 수 있어요.