Coding/C

[C언어] 파일 포인터 사용하기 - 코딩도장

GunP4ng 2024. 3. 6. 23:37

[코딩도장] Unit 71


1. 파일 포인터 활용하기 (fseek, ftell)

1. 파일 포인터 이동하기 (fseek)

fseek 함수는 기준점에서 이동할 거리(크기)를 지정하여 파일 포인터를 이동시킨다

  • fseek(파일포인터, 이동할크기, 기준점)
fseek(fp, 0, SEEK_END);    // 파일 포인터를 파일의 끝으로 이동시킴

fseek 의 기준점은

  • SEEK_SET    파일의 처음부터 시작
  • SEEK_CUR    현재 위치부터 이동 시작
  • SEEK_END    파일의 끝부터 시작

SEEK_SET, SEEK_CUR, SEEK_END는

기준점을 기준으로 파일 포인터의 위치를 바꾼다

 

fseek 함수의 기준점

fseek(fp, 0, SEEK_SET); 이면 파일의 처음으로 간다

fssek(fp, 0, SEEK_END); 이면 파일의 끝으로 간다

 

현재 위치에서 순방향으로 10바이트 이동하려면

fseek(fp, 10, SEKK_CUR); 과 같이 10바이트만큼 순방향으로 이동하도록 하면 된다

 

역방향으로 10바이트 이동하려면

fssek(fp, -10, SEEK_CUR); 과 같이 음수를 지정하면 된다

 

fseek 함수의 사용

파일 포인터의 이동 방향은 포인터 연산의 이동 방향과 같다

 

2. 파일 크기 구하기 (ftell)

파일포인터의 현재 위치를 얻어오려면 ftell 함수를 사용하면 된다

  • ftell(파일포인터)
size = ftell(fp);          // 파일 포인터의 현재 위치를 얻음

이동할 크기를 0으로하고 기준점을 SEEK_END로 지정하여

ftell 함수를 사용하면 파일의 크기를 알 수 있다

 

fseek(fp, 0, SEEK_END);    // 파일 포인터를 파일의 끝으로 이동시킴
size = ftell(fp);          // 파일 포인터의 현재 위치를 얻음

 

fseek, ftell 함수로 파일 크기 구하기

 

 

2. 파일 크기만큼 파일 읽기

1. 파일 크기만큼 파일 읽기

FILE *fp = fopen("hello.txt", "r");    // hello.txt 파일을 읽기 모드(r)로 열기.
                                       // 파일 포인터를 반환
  
fseek(fp, 0, SEEK_END);    // 파일 포인터를 파일의 끝으로 이동시킴
size = ftell(fp);          // 파일 포인터의 현재 위치를 얻음

fopen 함수로 파일을 읽기 모드로 열고

fseek 함수와 ftell 함수로 파일의 크기를 구한다

 

buffer = malloc(size + 1);    // 파일 크기 + 1바이트(문자열 마지막의 NULL)만큼 동적 메모리 할당
memset(buffer, 0, size + 1);  // 파일 크기 + 1바이트만큼 메모리를 0으로 초기화

파일의 문자열을 읽어서 C언어 문자열로 만들 때는

문자열 마지막의 NULL 공간까지 확보해야 한다

 

따라서 파일 크기 + 1바이트만큼 메모리를 할당한다

malloc 함수로 할당한 메모리는 memset 함수를 사용하여 0으로 초기화 한다

NULL공간 확보 후, NULL로 초기화

할당한 메모리를 NULL 로 초기화하지 않으면

문자열 끝 부분에 다른 값이 들어있을 수도 있다

문자열의 끝인 NULL을 찾을 수 없어 문자열 이외의 값이 함께 출력되므로 주의해야 한다

 

fseek(fp, 0, SEEK_SET);                // 파일 포인터를 파일의 처음으로 이동시킴
count = fread(buffer, size, 1, fp);    // hello.txt에서 파일 크기만큼 값을 읽음

파일의 크기를 구할 때 파일 포인터를 끝으로 이동시켰다

이 상태에서는 파일을 읽을 수 없기 때문에

fseek 함수를 이용하여 파일 포인터를 맨 처음으로 이동시킨다

 

printf("%s size: %d, count: %d\n", buffer, size, count);
                // Hello world! size: 13, count: 1: 파일의 내용, 파일 크기, 읽은 횟수 출력

printf 로 버퍼의 내용을 출력하면 파일의 내용이 출력된다

 

fclose(fp);      // 파일 포인터 닫기

free(buffer);    // 동적 메모리 해제

파일 읽기가 끝났으면 파일 포인터를 닫고 메모리를 해제한다

 

2. 제한된 버퍼로 파일 전체 읽기 (feof)

feof 함수는 현재 파일 포인터가 파일의 끝인지 검사한다

  • feof(파일포인터)
while (feof(fp) == 0)    // 파일 포인터가 파일의 끝이 아닐 때 계속 반복

feof 함수는 파일 포인터가 파일의 끝이면 1을 반환 아니면 0을 반환한다

 

char buffer[5] = { 0, };    // 문자열 데이터 4바이트 NULL 1바이트. 4 + 1 = 5

버퍼 크기를 5바이트로 만들어준다

문자열을 읽어서 출력할 것이기 때문에 문자열 4바이트 NULL 1바이트이다

 

while (feof(fp) == 0)    // 파일 포인터가 파일의 끝이 아닐 때 계속 반복
{
    count = fread(buffer, sizeof(char), 4, fp);    // 1바이트씩 4번(4바이트) 읽기
    
    printf("%s", buffer);                          // 읽은 내용 출력
    memset(buffer, 0, 5);                          // 버퍼를 0으로 초기화
    total += count;                                // 읽은 크기 누적
}

while 반복문에 조건식으로 feof(fp) == 10 을 지정하여

파일 포인터가 파일의 끝이 아닐 때 계속 반복하도록 한다

 

fread 함수로 파일을 읽을 때는 buffer 를 선언한 자료형이 char 이기 때문에

sizeof(char)를 지정하여 1바이트 크기로 4번 읽는다

fread 로 파일을 읽었을 때 읽은 크기만큼 반환값이 나오기 때문에 파일의 전체 크기를 구할 수 있다

 

fread 로 파일을 읽고 크기는 count에 저장한다

printf 로 buffer 의 내용을 출력한다

출력이 끝났다면 memset 함수로 buffer 를 초기화 한다

 

이렇게 한정된 버퍼를 이용해 전체 문자열을 출력할 수 있다

 

 

3. 심사문제

71.9 파일을 부분적으로 읽기

문자열이 저장된 words.txt 파일이 주어집니다. 다음 소스 코드에서 getData 함수를 완성하여 words.txt에서 읽은 문자열을 출력하고, 그 다음 줄에 읽은 크기를 출력하는 프로그램을 완성하세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int getFileSize(FILE *fp)
{
    int size;
    int currPos = ftell(fp);

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);

    fseek(fp, currPos, SEEK_SET);

    return size;
}

char *getData(int offset, int size, int *count, FILE *fp)
{
    __________________________________
    __________________________________

    __________________________________
    __________________________________

    __________________________________
}

int main()
{
    char *buffer;
    int size;
    int count;

    FILE *fp = fopen("words.txt", "r");

    size = getFileSize(fp);
    buffer = getData(0, size, &count, fp);
 
    printf("%s\n", buffer);
    printf("%d", count);

    fclose(fp);

    free(buffer);

    return 0;
}

 

 

정답

char *buffer;
    
buffer = malloc(size + 1);
memset(buffer, 0, size + 1);

fseek(fp, offset, SEEK_SET);
*count = fread(buffer, sizeof(char), size, fp);

return buffer;

71.10 파일을 부분적으로 읽기

문자열이 저장된 words.txt 파일이 주어집니다.(반드시 GitHub 저장소의 Unit 71/words.txt 파일로 테스트하세요.) 파일 처음부터 순방향으로 7바이트 지점에서 4바이트만큼 읽고, 파일 끝에서 역방향으로 6바이트 지점에서 2바이트만큼 읽은 값을 출력하는 프로그램을 만드세요. 단, 읽은 문자열은 공백으로 띄우지 않고 붙여서 출력합니다.

 

정답

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *readForword(char *buffer, int offset, int size, FILE *fp) {
    if (feof(fp) == 1)
        return NULL;
    
    fseek(fp, offset, SEEK_SET);
    fread(buffer, sizeof(char), size, fp);

    return buffer;
}

char *readBackword(char *buffer, int offset, int size, FILE *fp) {
    if (feof(fp) == 1)
        return NULL;

    fseek(fp, offset, SEEK_END);
    fread(buffer, sizeof(char), size, fp);
}

int main() {
    char forBuffer[20] = {0, };
    char backBuffer[10] = {0, };

    FILE *fp = fopen("words.txt", "r");

    readForword(forBuffer, 7, 4, fp);
    readBackword(backBuffer, -6, 2, fp);

    strcat(forBuffer, backBuffer);
    printf("%s", forBuffer);
    
    fclose(fp);

    return 0;
}

파일을 정방향으로 읽는 함수 readForword 를 작성하고

파일을 역방향으로 읽는 함수 readBackword 를 작성한다

두 함수를 이용해 문자열을 읽어온 뒤

두 문자열을 strcat 함수를 이용해 이어붙인다


71.11 파일을 읽은 뒤 거꾸로 저장하기

문자열이 저장된 words.txt 파일이 주어집니다. 파일을 읽은 뒤 내용을 거꾸로 저장하는 프로그램을 만드세요. 결과는 fwrite 함수로 stdout에 저장하면 됩니다. 단, words.txt 의 최대 크기는 10,000바이트입니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int size, readSize;
    char *buffer;

    FILE *fp = fopen("words.txt", "r");

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);

    buffer = malloc(size);

    while (size > 0) {
        fseek(fp, -1, SEEK_CUR);
        readSize = fread(buffer, sizeof(char), 1, fp);

        fwrite(buffer, sizeof(char), 1, stdout);

        fseek(fp, -1, SEEK_CUR);
        size --;
    }
    free(buffer);
    fclose(fp);

    return 0;
}

fseek 함수를 이용해 파일 포인터를 맨 끝으로 이동시킨 후

문자열을 거꾸로 읽어와 버퍼에 저장한다

 

하지만 fread 함수는 순방향으로 1 이동하기 때문에

문자열을 읽어온 후 fseek 함수를 이용해 역방향으로 1 이동시켜줘야 한다

(아니면 제자리에서 무한 반복하는 문제가 발생한다)