본문 바로가기
c언어

c언어 스트림과 파일 입출력을 이용하여 여러가지 문제 풀어보기(파일에서 특정 문자열 탐색하기, 이진 파일에 학생 정보 저장하기(데이터베이스 흉내내기), 이미지 파일 복사하기(이진 파일 ..

by 개발자 L 2023. 2. 17.
반응형

c언어 스트림과 파일 입출력을 이용하여 여러 가지 문제 풀어보기(파일에서 특정 문자열 탐색하기, 이진 파일에 학생 정보 저장하기(데이터베이스 흉내내기), 이미지 파일 복사하기(이진 파일 복사하기), 주소록 만들기)

네 안녕하세요, 이번 포스팅에서는 지금까지 배웠던 스트림과 파일 입출력을 이용해서

여러 가지 문제들을 풀어보는 시간을 가져보도록 하겠습니다.

파일 처리는 실전 프로그래밍을 할 때 반드시 필요한 항목이기 때문에 잘 알아두셔야 합니다.

그럼 지금부터 시작하도록 하겠습니다.

 

1. 파일에서 특정 문자열 탐색하기

제일 먼저 해 볼 일은 파일에서 특정한 문자열을 검색하는 것입니다.

이를 처리할 때 쓸 함수는 strstr() 함수를 이용해서 문자 배열 안에서 찾고자 하는 문자열이 있는지 검사하고,

있다면 현재 줄 번호를 화면에 출력을 할 것입니다.

그럼 바로 보여드리도록 하겠습니다.

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

int main()
{
    FILE *fp;
    char fname[120];
    char buffer[256];
    char word[256];
    int line_num = 0;

    printf("입력 파일의 이름을 입력하세요 : ");
    scanf("%s", fname);

    printf("탐색할 단어를 입력하세요 : ");
    scanf("%s", word);

    // 파일을 읽기 모드로 오픈
    if((fp = fopen(fname, "r")) == NULL)
    {
        fprintf(stderr, "파일 %s 를 찾을 수 없습니다.\n", fname);

        exit(1);
    }

    while(fgets(buffer, 256, fp))
    {
        line_num++;

        if(strstr(buffer, word))
        {
            printf("%s : %d 번째 줄에 단어 %s 가 발견되었습니다.\n", fname, line_num, word);
        }
    }

    fclose(fp);

    return 0;
}

이렇게 한 번 작성을 해봤습니다.

그럼 결과를 보여드리도록 하겠습니다.

우선 제가 작성한 파일입니다.

이렇게 작성을 했습니다.

그럼 결과를 보여드리도록 하겠습니다.

입력 파일의 이름을 입력하세요 : proverbs.txt
탐색할 단어를 입력하세요 : bird
proverbs.txt : 3 번째 줄에 단어 bird 가 발견되었습니다.

이렇게 잘 나온 것을 볼 수 있습니다.

 

2. 이진 파일에 학생 정보 입력하기(데이터베이스 흉내내기)

이번에는 이진 파일에 학생 정보를 저장해보도록 하겠습니다.

이건 데이터베이스와 관련이 있는 건데,

데이터베이스는 데이터들을 모아놓은 집합체입니다.

그래서 우리가 프로그래밍을 할 때 소위 '약방의 감초'라고 표현을 합니다.

그 이유는 이 저장된 데이터를 이용해서 정보를 찾고,

연결된 데이터들을 통해 사용자가 원하는 결과를 내어주며,

관리자들은 이 데이터를 통해 데이터 추가, 변경, 삭제 등을 할 수 있습니다.

그래서 다방면에서 쓸모가 많으며,

파일 처리와 더불어 가장 중요한 요소입니다.

그렇지만 이번에는 간단히 흉내만 내어보는 시간을 해보도록 하겠습니다.

아직 데이터베이스를 배우지 않은 분들도 있을 수 있으니까요.

그럼 바로 시작해보도록 하겠습니다.

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

#define SIZE 5

struct student
{
    int number;
    char name[20];
    double gpa;
};

int main()
{
    struct student table[SIZE] = {
        {1, "Kim", 3.99},
        {2, "Lee", 2.88},
        {3, "Park", 2.65},
        {4, "Baek", 1.89},
        {5, "Choi", 3.76}
    };

    struct student s;
    FILE *fp = NULL;
    int i;

    // 이진 파일을 쓰기 모드로 오픈
    if((fp = fopen("student.dat", "wb")) == NULL)
    {
        fprintf(stderr, "출력을 위한 파일을 열 수 없습니다.\n");

        exit(1);
    }

    // 배열을 파일에 저장
    fwrite(table, sizeof(struct student), SIZE, fp);
    
    fclose(fp);

    // 이진 파일을 읽기 모드로 오픈
    if((fp = fopen("student.dat", "rb")) == NULL)
    {
        fprintf(stderr, "입력을 위한 파일을 열 수 없습니다.\n");

        exit(1);
    }

    for(i = 0; i < SIZE; i++)
    {
        fread(&s, sizeof(struct student), 1, fp);

        printf("학번 = %d, 이름 = %s, 평점 = %f\n", s.number, s.name, s.gpa);
    }

    fclose(fp);

    return 0;
}

이렇게 한 번 작성을 해봤습니다.

그럼 결과를 보도록 하겠습니다.

student.dat 생성

가장 아래에 보면 student.dat이 생성이 된 걸 확인할 수 있고,

터미널 창에 나온 결과도 확인해 보도록 하겠습니다.

학번 = 1, 이름 = Kim, 평점 = 3.990000
학번 = 2, 이름 = Lee, 평점 = 2.880000
학번 = 3, 이름 = Park, 평점 = 2.650000
학번 = 4, 이름 = Baek, 평점 = 1.890000
학번 = 5, 이름 = Choi, 평점 = 3.760000

결과도 잘 나온 것을 볼 수 있습니다.

반응형

 

3. 이미지 파일 복사하기(이진 파일 복사하기)

이번에는 이미지 파일을 복사하는 코드를 작성해 보도록 하겠습니다.

기본적으로 이미지 파일은 이진 파일입니다.

그래서 이진 파일을 복사하는 방법이라 해도 되겠네요.

그럼 바로 시작해보도록 하겠습니다.

#include <stdio.h>

int main()
{
    FILE *src_file, *dst_file;
    char filename[100];
    char buffer[1024];
    int r_count;

    printf("이미지 파일 이름을 입력하세요 : ");
    scanf("%s", filename);

    src_file = fopen(filename, "rb");
    dst_file = fopen("copy.jpg", "wb");

    if(src_file == NULL || dst_file == NULL)
    {
        fprintf(stderr, "파일 열기 오류\n");

        return 1;
    }

    while((r_count = fread(buffer, 1, sizeof(buffer), src_file)) > 0)
    {
        int w_count = fwrite(buffer, 1, r_count, dst_file);

        if(w_count < 0)
        {
            fprintf(stderr, "파일 쓰기 오류\n");

            return 1;
        }

        if(w_count < r_count)
        {
            fprintf(stderr, "미디어 쓰기 오류\n");

            return 1;
        }
    }

    printf("copy.jpg로 이미지 파일이 복사되었습니다.\n");

    fclose(src_file);
    fclose(dst_file);

    return 0;
}

코드는 이렇게 작성을 했습니다.

그럼 결과를 바로 보여드리도록 하겠습니다.

이미지 파일 이름을 입력하세요 : zebra.jpg
copy.jpg로 이미지 파일이 복사되었습니다.

여기서 zebra.jpg를 copy.jpg로 복사를 했고, 성공을 했다고 뜹니다.

그럼 바로 결과를 확인해보도록 하겠습니다.

이미지 원본

이게 제가 가지고 있던 원본 사진입니다.

그럼 이게 copy.jpg로 복사가 되었는지 보도록 하겠습니다.

원본을 복사하여 생성된 이미지

복사 역시 잘 된 것을 확인할 수 있습니다.

 

4. 주소록 만들기

이번에는 주소록을 만들어보도록 하겠습니다.

이 주소록도 데이터베이스와 관련이 있고,

정말 많이 쓰는 방법입니다.

그리고 여기서 중점적으로 쓰는 파일 모드는 '추가 모드'입니다.

또한 추가 모드를 할 때 fseek() 함수를 쓰지 않으면 변경이 안된다는 사실 또한 잊지 마세요.

그럼 바로 시작해 보도록 하겠습니다.

#include <stdio.h>
#include <string.h>

#define SIZE 100

typedef struct person
{
    char name[SIZE];
    char address[SIZE];
    char mobile[SIZE];
    char desc[SIZE];
} PERSON;

void menu();
PERSON get_record();
void print_record(PERSON data);
void add_record(FILE* fp);
void search_record(FILE* fp);

int main()
{
    FILE* fp;
    int select;

    // 이진 파일을 추가 모드로 오픈
    if ((fp = fopen("address.dat", "a+")) == NULL)
    {
        fprintf(stderr, "입력을 위한 파일을 열 수 없습니다.\n");

        exit(1);
    }

    while (1)
    {
        menu();
        printf("정수값을 입력하세요 : "); // 사용자로부터 정수값 입력(id)
        scanf("%d", &select);

        switch (select)
        {
        case 1: add_record(fp);
        {
            break;
        }

        case 2: search_record(fp);
        {
            break;
        }

        case 3: return 0;
        }
    }

    fclose(fp);

    return 0;
}

// 사용자로부터 데이터를 받아 구조체 반환
PERSON get_record()
{
    PERSON data;

    getchar();

    printf("이름 : ");
    gets_s(data.name, SIZE);

    printf("주소 : ");
    gets_s(data.address, SIZE);

    printf("휴대폰 : ");
    gets_s(data.mobile, SIZE);

    printf("특징 : ");
    gets_s(data.desc, SIZE);

    return data;
}

// 구조체 데이터 화면에 출력
void print_record(PERSON data)
{
    printf("이름 : %s\n", data.name);
    printf("주소 : %s\n", data.address);
    printf("휴대폰 : %s\n", data.mobile);
    printf("특징 : %s\n", data.desc);
}

// 메뉴를 화면에 출력
void menu()
{
    printf("==============================\n");
    printf("1. 추가\n 2. 검색\n 3. 종료\n");
    printf("==============================\n");
}

// 데이터 추가
void add_record(FILE* fp)
{
    PERSON data;
    data = get_record();
    fseek(fp, 0, SEEK_END);
    fwrite(&data, sizeof(data), 1, fp);
}

void search_record(FILE* fp)
{
    char name[SIZE];
    PERSON data;

    fseek(fp, 0, SEEK_SET);
    getchar();
    printf("탐색하고자 하는 사람의 이름을 입력하세요 : ");
    gets_s(name, SIZE);

    while (!feof(fp))
    {
        fread(&data, sizeof(data), 1, fp);

        if (strcmp(data.name, name) == 0)
        {
            print_record(data);

            break;
        }
    }
}

이렇게 한 번 적어봤습니다.

사실 디테일하게 하려면 수정과 삭제 기능도 넣을 수 있지만,

그건 난도가 있기 때문에 구조 정도만 보여드리기 위해 뺐습니다.

그럼 결과를 보도록 하겠습니다.

==============================
1. 추가
 2. 검색
 3. 종료
==============================
정수값을 입력하세요 : 1
이름 : 홍길동
주소 : 서울시 종로구 1번지
휴대폰 : 010-1234-5678
특징 : 싸움 잘함, 변신에 능함
==============================
1. 추가
 2. 검색
 3. 종료
==============================
정수값을 입력하세요 : 2
탐색하고자 하는 사람의 이름을 입력하세요 : 홍길동
이름 : 홍길동
주소 : 서울시 종로구 1번지
휴대폰 : 010-1234-5678
특징 : 싸움 잘함, 변신에 능함
==============================
1. 추가
 2. 검색
 3. 종료
==============================
정수값을 입력하세요 : 3

C:\C언어 연습\Project5\x64\Debug\Project5.exe(프로세스 30260개)이(가) 종료되었습니다(코드: 0개).

이런 식으로 결과가 잘 나온 것을 볼 수 있습니다.

 

지금까지 스트림과 파일 입출력에 대하여 알아보았는데요,

다음 포스팅부터는 전처리 및 다중 소스 파일에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사드립니다~

반응형

댓글