C언어 study_문자열 단위로 파일 입출력하기

민성철·2022년 11월 24일
0

nadocoding_study_C

목록 보기
16/17

파일에 데이터를 저장하는 것을 "파일 쓰기", 저장한 내용을 불러오는 것을 "파일 읽기"라고 합니다.
C언어에서는 파일을 읽고 쓸 때는 한쌍으로 이루어져 있는 fputs(), fgets() 함수와 fprintf(), fscanf() 함수를 사용합니다.
위 함수들은 섞어 써도 무방합니다

파일에서 데이터를 읽고 쓸 때는 먼저 fopen() 함수로 파일을 열어서 파일 포인터를 얻어야 합니다.
파일 포인터는 무슨 파일을 열고, 어디까지 읽고, 파일의 끝에 도달했는지 등의 정보를 담습니다.

파일 포인터를 얻기 위해서는 FILE을 먼저 적어주고 * 뒤에 포인터 명을 적어줍니다. 그리고 등호 표시(=) 뒤에 fopen() 함수를 써주고 함수 소괄호 안에 순서대로 파일명과 파일 모드를 적어줍니다.
파일명을 적어줄 때는 원하는 파일의 경로까지 적어줍니다.
여기서 주의할 점은 역슬래시()의 경우 문자와 숫자를 조합하면 특수 문자를 표현하기 때문에, 역슬래시를 두 번 넣어줘야 합니다.

이해를 돕기 위한 예시 코드는 아래와 같습니다.

FILE * 포인터명 = fopen(파일명, 파일모드);
FILE * file = fopen("C:\\Users\\사용자계정\\test.txt", "wb")

  • 텍스트는 사람이 읽을 수 있는 문자열을 뜻하고, 바이너리는 컴퓨터가 읽을 수 있는 0과 1로 된 데이터입니다.

  • 보통 파일을 여는 fopen() 함수를 사용한 뒤에 if문을 활용하여, fopen() 함수가 제대로 실행되었는지 확인합니다.

int main(void) {
	FILE * file = fopen("C:\\Users\\사용자계정\\test.txt", "wb")
    if (file == NULL) {
    	printf("파일 열기 실패");
        return 1;
    }
    return 0;
}

파일이 정상적으로 생성되었다면 이제 파일에 데이터를 읽고 쓸 수 있습니다.

fputs() 함수로 파일 쓰기

fputs() 함수는 문자열 단위로 저장합니다.
fputs() 함수는 소괄호 안에 순서대로 저장할 문들과 파일 포인터를 넣습니다.
또한, 파일을 열고 문자를 입력한 뒤에는 꼭 파일을 닫아줘야 합니다.
그렇지 않고 프로그램에 문제가 생긴다면 데이터 손실이 발생할 수도 있기 때문입니다.
파일을 닫을 때는 fclose() 함수를 사용하며, 소괄호 안에 열었던 파일의 포인터를 넣어줘야 합니다.

아래 예시 코드입니다.

int main(void) {
	FILE * file = fopen("C:\\Users\\사용자계정\\test.txt", "wb")
    if (file == NULL) {
    	printf("파일 열기 실패");
        return 1;
    }
    
    fputs("오늘은 C언어의 pointer를 공부했습니다", file);
    fputs("내일은 C언어의 struct을 공부할 예정입니다", file);
    fclose(file);
    return 0;
}

fgets() 함수로 파일 읽기

파일에 내용을 썼으니 파일에 쓴 내용을 읽어보겠습니다.

이때 파일 모드를 바이너리 데이터의 쓰기 전용이 아닌, 일기 전용(rb)으로 설정해야 합니다.
읽기 위한 함수는 fgets()로 소괄호 안에는 순서대로 변수명, 문자열의 최대 크기, 파일 포인터를 적어줍니다.

!. 여기서 잠깐 알아가야 할 전처리기 지시문이 있습니다.

  • #define
    #define은 다음과 같은 형식으로 매크로(macro)를 정의하는 전처리기 지시문입니다.
    매크로는 소스 코드 안에서 값을 대체해 사용하는 문자열을 의미합니다.
    매크로는 코드에 직접 정의할 수도 있고, C언어에 내장된 매크로를 불러와 정의할 수도 있습니다.
    또한, define을 이용하면 전체 소스 코드 내 매크로를 일괄적으로 대체 상수로 치환하게 되므로 필요시 대체 상수의 값을 한 번만 변경하면 모든 곳에서 손쉽게 변경 사항을 적용할 수 있습니다.
    예를 들어 다음과 같이 간단한 연산을 수행하는 매크로 함수도 만들 수 있습니다.
#include <stdio.h>
#define ADD(x1, x2) (x1 + x2)

int main(void) {
	int result = ADD(10, 20);
    printf("%d\n", result);
    return 0;
}
  • 이렇게 작성하면 ADD(10, 20)이라는 코드를 (10 + 20)으로 치환하게 됩니다.
    매크로 함수는 자료형에 독립적이기 때문에 ADD(10.1f, 20.2f)와 같이 실수 연산도 할 수 있습니다.
    또한, 전처리기에 의해 단순히 치환되는 방식이라서 실행 속도가 향상됩니다.

이제 위에서 문자를 읽어오는 fgets() 함수의 문자열 크기를 #define을 활용하여 작성해보겠습니다.

#include <stdio.h>
#define MAX 1000

int main(void) {
	char line[MAX]; // 문자열 크기를 #define을 통해 10000으로 설정
    FILE * file("C:\\Users\\사용자계정\\test.txt", "rb");
    if (file == NULL) {
    	printf("파이 읽기 실패\n");
        return 1;
    }
    while (fgets(line, MAX, file) != NULL) {
    	printf("%s", line);
    }
    fclose(file);
    return 0;
}

output

오늘은 C언어의 pointer를 공부했습니다
내일은 C언어의 struct을 공부할 예정입니다

fgets() 함수는 파일에 저장된 내용을 문자열 단위로 읽습니다.
그리고 한 번 읽을 때 줄 단위로 읽는 것이 아닌, 함수 두 번째에 해당하는 문자열에 최대 크기만큼 읽습니다.
또한, 문자열에는 마지막에 줄 바꿈(\n)이 포함되어 있습니다.
fgets() 함수는 줄 바꿈과 문자열의 최대 크기 -1 만큼만 읽을 수 있습니다.
그래서 문자열의 최대 크기를 고려하여 설정해주고, 줄 바꿈이 있을 수 있기 때문에 위 코드처럼 while문을 통해서 읽어주는 게 좋습니다.

fprintf()함수와 fscanf()함수를 통해 형식을 지정하여 파일 입출력 하기

위에서 알아봤던 fputs() 함수와 fgets() 함수는 문자열 단위로 파일에 데이터를 활용했습니다.
이번에 배울 fprintf() 함수와 fscanf() 함수는 정해진 형식으로 파일에 데이터를 활용합니다.
위 함수는 앞전에 배웠던 함수와 비슷한 것을 알 수 있습니다.
fprintf() 함수는 printf() 함수를, fscanf() 함수는 scanf() 함수와 비슷합니다.
기존 printf() 함수와 scanf() 함수는 서식 지정자를 통해 변수, 즉 메모리에 저장된 값을 출력하거나 어떤 값을 입력받아 변수에 저장했습니다.
이와 마찬가지로 fprintf() 함수와 fscanf() 함수도 정해진 형식에 맞춰 파일에 데이터를 읽거나 쓸 때 사용합니다.

  • 먼저 fprintf() 함수는 파일에 데이터를 저장합니다.
    작성할 때는 소괄호 안에 순서대로 파일 포인터, "서식 지정자", 값을 넣어줍니다.
    여기서 주의할 점은 printf() 함수와 동일하게 서식 지정자와 값은 개수가 맞아야 합니다.

  • 다음으로 fscanf() 함수는 파일에서 데이터를 읽어 옵니다.
    작성할 때는 소괄호 안에 순서대로 파일 포인터, "서식 지정자", &변수명을 넣어줍니다.
    fprintf() 함수와 동일하게 서식 지정자와 &변수는 개수가 맞아야 합니다.
    또한, &변수의 경우에는 main() 함수 안에 알맞게 선언해줘야 합니다.

아래 예시 코드로 알아보겠습니다.

#include <stdio.h>

int main(void) {
	// 파일 모드는 바이너리 데이터를 쓰는 목적(wb)으로 지정합니다.
	FILE * file = ("C:\\Users\\사용자계정\\lotto.txt", "wb");
    if (file == NULL) {
    	printf("파일 열기 실패\n");
        return 1;
    }
    // fprintf()함수로 파일에 저장합니다.
    fprintf(file, "%s, %d %d %d %d %d %d\n", "추첨번호", 1, 2, 3, 4, 5, 6);
    fprintf(file, %s %d\n", "보너스번호", 7);
    fclose(file);
    return 0;
}

위 코드를 실행하게 되면 lotto.txt 파일에 아래와 같은 내용이 저장된 것을 확인할 수 있습니다.

lotto.txt 파일
추첨번호 1 2 3 4 5 6
보너스번호 7

하지만 별도로 컴파일러에서 출력되는 것은 없습니다.
출력하기 위해서 fscanf() 함수를 활용해보겠습니다.

#include <stdio.h>
#define MAX 10000

int main(void) {
	char str1[MAX];					// 출력하기 위해 변수 선언
    char str2[MAX];					
    int num[6] = {0, 0, 0, 0, 0, 0};
    int bonus = 0;
    // 이때 파일 모드를 바이너리 데이터의 쓰기 전용이 아닌, 읽기 전용(rb)으로 설정해야 합니다.
    FILE * file = ("C:\\Users\\사용자계정\\lotto.txt", "rb");
    if (file == NULL) {
    	printf("파일 읽기 실패\n");
        return 1;
    }
    // fscanf()함수로 lotto.txt파일의 내용을 읽어 옵니다.
    fscanf(file, "%s %d %d %d %d %d %d", str1, &num[0], &num[1], &num[2], &num[3], &num[4], &num[5]);
    fscanf(file, "%s %d", str2, &bonus);
    
    // fscanf()로 lotto.txt 파일의 내용을 읽어와 변수에 저장한 걸 printf() 함수를 통해 출력해보겠습니다.
    printf(("%s %d %d %d %d %d %d", str1, &num[0], &num[1], &num[2], &num[3], &num[4], &num[5]);
    printf("%s %d", str2, &bonus);
    fclose(file);
    return 0;
}
    

output

추첨번호 1 2 3 4 5 6
보너스번호 7
profile
ENTJ-A

0개의 댓글