[C언어] 도전! 프로그래밍 4

김민정·2024년 9월 25일
1
post-thumbnail

Chapter 28. 도전! 프로그래밍 4

도전1

간단한 도서 관리용 프로그램을 작성해보자.
[제목, 저자명, 페이지수]에 대한 정보를 저장할 수 있는 구조체를 정의하고, 구조체 배열을 선언해서 도서에 대한 정보를 저장하는 구조로 작성해 보자.
main 함수에서는 사용자로부터 3권의 도서에 대한 정보를 입력 받고, 입력이 끝나면 도서에 대한 내용을 출력해 주도록 하자.

  • 실행 예시
    도서 정보 입력
    저자: Yoon
    제목: C Programming
    페이지 수 : 200
    저자: Hong
    제목: C++ Programming
    페이지 수 : 250
    저자: James
    제목: OS for Programmer
    페이지 수 : 300

    도서 정보 출력
    book 1
    저자: Yoon
    제목: C Programming
    페이지 수 : 200
    book 2
    저자: Hong
    제목: C++ Programming
    페이지 수 : 250
    book 3
    저자: James
    제목: OS for Programmer
    페이지 수 : 300

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

struct Book {
    char title[50];
    char author[50];
    int page;
} Book;

int main()
{
    struct Book books[3];
    int i;

    printf("<도서 정보> - 입력\n");
    for(i = 0; i < 3; i++)
    {
        printf("저자: ");
        fgets(books[i].author, sizeof(books[i].author), stdin);
        strtok(books[i].author, "\n");  // 개행 문자 제거

        printf("제목: ");
        fgets(books[i].title, sizeof(books[i].title), stdin);
        strtok(books[i].title, "\n"); // 개행 문자 제거

        printf("페이지: ");
        scanf("%d", &books[i].page);
        getchar(); // 개행 문자 제거
        printf("\n");
    }

    printf("<도서 정보> - 출력\n");
    for(i = 0; i < 3; i++)
    {
        printf("도서 %d\n", i + 1);
        printf("제목: %s\n", books[i].title);
        printf("저자: %s\n", books[i].author);
        printf("페이지: %d\n", books[i].page);
        printf("\n");
    }

    return 0;
}

>출력
<도서 정보> - 입력
저자: Yoon
제목: C Programing
페이지: 200

저자: Hong
제목: C++ Programing
페이지: 250

저자: James
제목: OS for Programmer
페이지: 300

<도서 정보> - 출력
도서 1
제목: C Programing
저자: Yoon
페이지: 200

도서 2
제목: C++ Programing
저자: Hong
페이지: 250

도서 3
제목: OS for Programmer
저자: James
페이지: 300

-풀이-
처음에는 바로 printf와 scanf를 사용해서 문제를 풀려고 했는데 그렇게 하다보니 제목에서 띄어쓰기가 들어가면 페이지를 입력할 수 없게 되었다.
따라서 fgets함수를 이용해서 사용자로부터 입력받게 수정했다.
strtok함수는 string을 tokenize로 문자열(string)을 토큰(token)처럼 조각조각 내는 함수다. 쉼표(,)와 띄어쓰기( )를 구분자로 넣어서 띄어쓰기를 제외한 단어들을 가져올 수 있다.


도전 2

도전 1에서 구현한 프로그램에 약간의 변경을 줘보자.
구조체 배열을 선언하는 것이 아니라, 구조체 포인터 배열을 선언하고 구조체 변수를 동적으로 할당하는 형태로 프로그램을 재 구현해 보자.
그리고 도전 1에서 구현한 방법보다 도전 2에서 구현한 방법이 지니는 장점이 무엇인지도 생각해보자.

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

struct Book {
    char title[50];
    char author[50];
    int page;
};

int main() {
    struct Book **books;  // 구조체 포인터 배열
    int i;
    int num_books = 3;    // 도서 개수

    // 구조체 포인터 배열 동적 할당
    books = (struct Book **)malloc(num_books * sizeof(struct Book *));
    for (i = 0; i < num_books; i++) {
        books[i] = (struct Book *)malloc(sizeof(struct Book));
    }

    printf("<도서 정보> - 입력\n");
    for (i = 0; i < num_books; i++) {
        printf("저자: ");
        fgets(books[i]->author, sizeof(books[i]->author), stdin);
        strtok(books[i]->author, "\n");  // 개행 문자 제거

        printf("제목: ");
        fgets(books[i]->title, sizeof(books[i]->title), stdin);
        strtok(books[i]->title, "\n"); // 개행 문자 제거

        printf("페이지: ");
        scanf("%d", &books[i]->page);
        getchar(); // 개행 문자 제거
        printf("\n");
    }

    printf("<도서 정보> - 출력\n");
    for (i = 0; i < num_books; i++) {
        printf("도서 %d\n", i + 1);
        printf("제목: %s\n", books[i]->title);
        printf("저자: %s\n", books[i]->author);
        printf("페이지: %d\n", books[i]->page);
        printf("\n");
    }

    // 동적으로 할당한 메모리 해제
    for (i = 0; i < num_books; i++) {
        free(books[i]);
    }
    free(books);

    return 0;
}

>출력
<도서 정보> - 입력
저자: Yoon
제목: C Programing
페이지: 200

저자: Min
제목: Choregraphy Basic
페이지: 150

저자: Kim
제목: What is Love
페이지: 100

<도서 정보> - 출력
도서 1
제목: C Programing
저자: Yoon
페이지: 200

도서 2
제목: Choregraphy Basic
저자: Min
페이지: 150

도서 3
제목: What is Love
저자: Kim
페이지: 100

-풀이-
<도전 1과 도전 2 비교>

  1. 유연한 메모리 관리:
    도전 1 - 고정된 크기의 배열을 사용하여 메모리를 할당하므로, 프로그램 시작 시 도서의 개수를 정해야 함.
    도전 2 - 동적 메모리 할당을 사용하여 필요에 따라 도서의 개수를 조절할 수 있음. 이로 인해 메모리 사용이 더 효율적.

  2. 메모리 사용 최적화:
    도전 1 - 배열의 크기를 미리 정해야 하므로, 필요하지 않은 메모리가 낭비될 수 있음.
    도전 2 - 입력받는 도서의 수에 따라 메모리를 동적으로 조절할 수 있어, 필요한 만큼만 메모리를 사용할 수 있음.

  3. 재사용 가능성:
    도전 1 - 배열의 크기를 변경하려면 프로그램을 수정해야 함.
    도전 2 - 동적 할당을 사용하면, 프로그램 실행 중에 도서의 수를 쉽게 변경하거나 확장할 수 있음.

[결론]
동적 메모리 할당을 사용하면 더 유연하고 효율적인 메모리 관리가 가능하여, 다양한 조건에서 프로그램을 보다 쉽게 수정하고 확장할 수 있다.


도전 3

복소수(Complex Number)를 나타내는 구조체를 정의하고, 복소수의 덧셈과 곱셈을 위한 함수를 각각 정의하자. 그리고 이를 기반으로 프로그램 사용자로부터 두 개의 복소수 정보를 입력 받아서 두 복소수의 덧셈과 곱셈의 결과를 출력하는 프로그램을 작성하자.

  • 실행 예시
    복소수 입력1[실수 허수]: 1.2 2.4
    복소수 입력2[실수 허수]: 1.1 2.2
    합의 결과] 실수: 2.300000, 허수: 4.600000
    곱의 결과] 실수: -3.960000, 허수: 5.280000

  • 복소수 공식

    • 덧셈공식: (a+bi)+(c+di) = (a+c) + (b+d)i
    • 곱셈공식: (a+bi)*(c+di) = ac - bd + bci +adi
#include <stdio.h>

typedef struct {
    double real;
    double imaginary;
} ComplexNumber;

ComplexNumber addComplexNumbers(ComplexNumber num1, ComplexNumber num2)
{
    ComplexNumber result;
    result.real = num1.real + num2.real;
    result.imaginary = num1.imaginary + num2.imaginary;
    return result;
}

ComplexNumber multiplyComplexNumbers(ComplexNumber num1, ComplexNumber num2)
{
    ComplexNumber result;
    result.real = num1.real * num2.real - num1.imaginary * num2.imaginary;
    result.imaginary = num1.real * num2.imaginary + num1.imaginary * num2.real;
    return result;
}

int main()
{
    ComplexNumber num1, num2, sum, mul;
    printf("복소수 입력1[실수 허수]: ");
    scanf("%lf %lf", &num1.real, &num1.imaginary);
    printf("복소수 입력2[실수 허수]: ");
    scanf("%lf %lf", &num2.real, &num2.imaginary);
    
    sum = addComplexNumbers(num1, num2);
    mul = multiplyComplexNumbers(num1, num2);
    
    printf("합의 결과] 실수: %.5lf, 허수: %.5lf\n", sum.real, sum.imaginary);
    printf("곱의 결과] 실수: %.5lf, 허수: %.5lf\n", mul.real, mul.imaginary);
    
    return 0;
}

>출력
복소수 입력1[실수 허수]: 1.2 2.4
복소수 입력2[실수 허수]: 1.1 2.2
합의 결과] 실수: 2.30000, 허수: 4.60000
곱의 결과] 실수: -3.96000, 허수: 5.28000

-풀이-
복소수를 오랜만에 다시 보게 됐는데 단순하게 복소수의 덧셈 공식과 곱셈 공식을 함수로 만들어서 해결했다.


도전 4

문자열을 저장하고 있는 파일을 열어서 A와 P로 시작하는 단어의 수를 세어서 출력하는 프로그램을 작성해보자.
단, 모든 단어는 공백문자(space bar, \t, \n)에 의해서 구분된다고 가정한다.

  • 실행 예시
    실행파일의 이름이 wordcnt.exe이고 대상파일의 이름이 text.txt인 경우의 실행의 예
    명령어: .\wordcnt text.txt
    A로 시작하는 단어의 수: 4
    P로 시작하는 단어의 수: 3
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MAX_WORD_LENGTH 100

int main(int argc, char *argv[])    // argc: 명령행 인자의 수, argv: 명령행 인자를 문자열 배열로 저장.
{
    if (argc != 2)  // 파일 이름이 인자로 제공되지 않으면 오류 메세지 출력하고 종료.
    {
        printf("사용법: %s <파일이름>\n", argv[0]);
        return 1;
    }

    FILE *file = fopen(argv[1], "r");   // 파일 열기. 실패시 perror 함수 사용하여 오류 메세지 출력하고 종료.
    if (file == NULL)
    {
        perror("파일을 열 수 없습니다");
        return 1;
    }

    char word[MAX_WORD_LENGTH]; // 각 단어 저장.
    int countA = 0, countP = 0;

    while (fscanf(file, "%s", word) == 1)   // fscanf으로 파일에서 단어 하나씩 읽어오기. %s로 공백으로 구분된 단어 읽음.
    {
        // 첫 글자를 대문자로 변환하여 비교
        char firstChar = toupper(word[0]);  // 단어의 첫 글자 대문자로 변환.
        if (firstChar == 'A') {
            countA++;   // A로 시작하는 단어 카운트.
        } else if (firstChar == 'P') {
            countP++;   // P로 시작하는 단어 카운트.
        }
    }

    fclose(file);

    printf("A로 시작하는 단어의 수: %d\n", countA);
    printf("P로 시작하는 단어의 수: %d\n", countP);

    return 0;
}

>명령어 & 출력
>gcc -o wordcnt ch4.c    
>.\wordcnt text.txt
A로 시작하는 단어의 수: 2
P로 시작하는 단어의 수: 6

-풀이-
ctype.h 헤더 파일은 C 표준 라이브러리의 일부로, 문자 처리에 관련된 여러 함수를 제공한다. 이 파일에 포함된 함수들은 주로 문자 분류와 변환을 위한 기능을 제공하며, 다음과 같은 함수들이 포함되어 있다.

  • toupper(int c): 주어진 문자를 대문자로 변환. 만약 문자가 대문자일 경우 그대로 반환.
  • tolower(int c): 주어진 문자를 소문자로 변환. 만약 문자가 소문자일 경우 그대로 반환.
  • isalnum(int c), isalpha(int c), isdigit(int c) 등: 문자가 알파벳, 숫자, 또는 기타 특정 범주에 속하는지를 확인.

이 프로그램에서는 toupper() 함수를 사용하여 단어의 첫 글자를 대문자로 변환하고, A 또는 P와 비교하기 위해 사용했다.

참고로 나의 text.txt에는 아래와 같이 적었었다.

apple
avocado
banana
blackberry
blueberry
cherry tomato
cherry
coconut
grape
kiwi
lemon
lime
mango
melon
orange
papaya
peach
pear
persimmon
pineapple
plum
strawberry
tangerine
tomato
watermelon

도전 5

두 개의 텍스트 파일이 같은지 다른지를 확인하는 프로그램을 작성해 보자.
단순히 공백문자 하나가 차이를 보여도 두 텍스트 파일은 다른 것으로 판별이 나야 한다.

  • 실행 예시
    다음은 실행파일의 이름이 comp.exe이고 비교의 대상이 되는 두 파일의 이름이 각각 d1.txt와 d2.txt인 경우의 실행의 예이다.

명령어: .\comp d1.txt d2.txt
두 개의 파일은 완전히 일치 합니다.

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

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("사용법: %s <파일1> <파일2>\n", argv[0]);
        return 1;
    }

    FILE *file1 = fopen(argv[1], "r");
    FILE *file2 = fopen(argv[2], "r");
    
    if (file1 == NULL || file2 == NULL)
    {
        perror("파일을 열 수 없습니다");
        return 1;
    }

    char ch1, ch2;
    int areEqual = 1;  // 파일이 같다고 가정

    while ((ch1 = fgetc(file1)) != EOF && (ch2 = fgetc(file2)) != EOF)
        if (ch1 != ch2)
        {
            areEqual = 0;  // 파일이 다름
            break;
        }

    // 파일의 끝을 넘어선 경우: 하나의 파일이 더 길 경우
    if (areEqual && (fgetc(file1) != EOF || fgetc(file2) != EOF))
        areEqual = 0;  // 파일 길이가 다름

    fclose(file1);
    fclose(file2);

    if (areEqual)
        printf("두 개의 파일은 완전히 일치합니다.\n");
    else
        printf("두 개의 파일은 다릅니다.\n");

    return 0;
}

>명령어 & 출력
>gcc -o comp ch5.c
>.\comp text.txt text1.txt	//아까 위에서 사용한 파일 또 사용.
두 개의 파일은 완전히 일치합니다.

-풀이-
두 파일의 길이 또는 내용을 확인하는 것이 관건이었다.


도전 6

전화번호 관리 프로그램을 작성해 보자.
이 프로그램이 기본적으로 지녀야 하는 기능은 다음과 같다.

  • 입력: 이름과 전화번호의 입력
  • 삭제: 이름을 입력하여 해당 이름의 정보 삭제
  • 검색: 이름을 입력하여 해당 이름의 정보 출력
  • 전체 출력: 저장된 모든 이름과 전화번호 정보를 출력

실행 예시와 비슷하게 동작하는 전화번호 관리 프로그램을 구현하기 바란다.

  • 실행 예시
*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Yoon
Input Tel Number: 333-4444
				Data Inserted
                
*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 2
[DELETE]
Input Name that You Want to Delete: Yoon
				Data Deleted
                
*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 3
[SEARCH]
Input Name that You Want to Find: Yoon
[Data]
Name: Yoon
Tel: 333-4444
				Data Searched

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 4
[Print All Data]
Name: Yoon	Tel: 333-4444
				Data Printed
실제 문제의 실행 예시에서 insert와 print all data만 주어졌다.
// ch6_func.h
#ifndef CH6_FUNC_H
#define CH6_FUNC_H

#define MAX_CONTACTS 100
#define NAME_LENGTH 50
#define TEL_LENGTH 15

// 연락처 구조체 정의
typedef struct {
    char name[NAME_LENGTH];
    char tel[TEL_LENGTH];
} Contact;

// 함수 선언
void insertContact();
void deleteContact();
void searchContact();
void printAllContacts();

#endif
// ch6_func.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ch6_func.h"   // 헤더 파일 포함

#define MAX_CONTACTS 100
#define NAME_LENGTH 50
#define TEL_LENGTH 15

// 전화번호 목록
Contact contacts[MAX_CONTACTS];
int contactCount = 0;

void InsertContact()
{
    if(contactCount >= MAX_CONTACTS)
    {
        printf("전화번호 목록이 가득 찼다.(100/100)\n\n");
        return;
    }

    printf("[INSERT]\n");
    printf("Input Name: ");
    fgets(contacts[contactCount].name, NAME_LENGTH, stdin);
    strtok(contacts[contactCount].name, "\n");

    printf("Input Tel Number: ");
    fgets(contacts[contactCount].tel, TEL_LENGTH, stdin);
    strtok(contacts[contactCount].tel, "\n");

    contactCount++;
    printf("\t\t\tData Inserted\n\n");
}

void DeleteContact()
{
    char name[NAME_LENGTH];
    printf("[DELETE]\n");
    printf("Input Name that You Want to Delete: ");
    fgets(name, NAME_LENGTH, stdin);
    strtok(name, "\n");

    for(int i = 0; i < contactCount; i++)
    {
        if(strcmp(contacts[i].name, name) == 0)
        {
            for(int j = i; j < contactCount - 1; j++)
            {
                strcpy(contacts[j].name, contacts[j + 1].name);
                strcpy(contacts[j].tel, contacts[j + 1].tel);
            }
            contactCount--;
            printf("\t\t\tData Deleted\n\n");
            return;
        }
    }
    printf("No such contact found with the name '%s'\n\n", name);
}

void SearchContact()
{
    char name[NAME_LENGTH];
    printf("[SEARCH]\n");
    printf("Input Name that You Want to Find: ");
    fgets(name, NAME_LENGTH, stdin);
    strtok(name, "\n");
    
    for(int i = 0; i < contactCount; i++)
    {
        if(strcmp(contacts[i].name, name) == 0)
        {
            printf("[Data]\n");
            printf("Name: %s\n", contacts[i].name);
            printf("Tel: %s\n", contacts[i].tel);
            printf("\t\t\tData Searched\n\n");
            return;
        }
    }
    printf("No such contact found with the name '%s'\n\n", name);
}

void PrintAllContacts()
{
    printf("[Print All Data]\n");
    for(int i = 0; i < contactCount; i++)
    {
        printf("Name: %s\tTel: %s\n", contacts[i].name, contacts[i].tel);
    }
    printf("\t\t\tData Printed\n\n");
}
// ch6.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 함수 참조
void InsertContact();
void DeleteContact();
void SearchContact();
void PrintAllContacts();

int main()
{
    int choice;

    while(1)
    {
        printf("*****MENU*****\n");
        printf("1. Insert\n");
        printf("2. Delete\n");
        printf("3. Search\n");
        printf("4. Print All\n");
        printf("5. Exit\n");
        printf("Choose the item: ");
        scanf("%d", &choice);
        getchar();

        switch(choice)
        {
            case 1:
                InsertContact();
                break;
            case 2:
                DeleteContact();
                break;
            case 3:
                SearchContact();
                break;
            case 4:
                PrintAllContacts();
                break;
            case 5:
                printf("Exiting...\n");
                return 0;
            default:
                printf("Invalid choice! Please try again.\n\n");
        }
    }
}
>명령어 & 출력
>gcc -o phonebook ch6.c ch6_func.c
>.\phonebook.exe
*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Kim
Input Tel Number: 111-2222
                        Data Inserted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 2
[DELETE]
Input Name that You Want to Delete: Min
No such contact found with the name 'Min'

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 2
[DELETE]
Input Name that You Want to Delete: Kim
                        Data Deleted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Kim
Input Tel Number: 111-2222
                        Data Inserted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Min
Input Tel Number: 222-3333
                        Data Inserted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 3
[SEARCH]
Input Name that You Want to Find: Jung
No such contact found with the name 'Jung'

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 3  
[SEARCH]
Input Name that You Want to Find: Min
[Data]
Name: Min
Tel: 222-3333
                        Data Searched

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 4
[Print All Data]
Name: Kim       Tel: 111-2222
Name: Min       Tel: 222-3333
                        Data Printed

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 6
Invalid choice! Please try again.
*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 5
Exiting...

-풀이-
나는 Part 4에서 배운 내용을 사용하기 위해서 헤더 파일과 다른 파일로 나누어 이번 문제를 해결해보려 했다.
그리고 delete와 search를 했을 때는 기존에 작성한 데이터와 맞는지 안맞는지 경우에 따라 다른 알림 문장을 보여주려 했다.


도전 7

도전 6에서 구현한 프로그램의 문제점은 프로그램이 종료되고 나면 기존에 저장된 데이터가 전부 사라진다는 것이다. 이 문제점을 해결하자.
프로그램이 종료되기 전에 파일을 하나 생성해서 기존에 입력받은 데이터를 저장하고, 프로그램을 다시 실행하면 파일에 저장된 데이터를 읽어 들이는 방식으로 프로그램을 변경해보자.

// ch7_func.h
#ifndef CH7_FUNC_H
#define CH7_FUNC_H

#define MAX_CONTACTS 100
#define NAME_LENGTH 50
#define TEL_LENGTH 15

typedef struct 
{
    char name[NAME_LENGTH];
    char tel[TEL_LENGTH];
} Contact;

void InsertContact();
void DeleteContact();
void SearchContact();
void PrintAllContacts();
void SaveContacts();    // 파일 저장 함수 추가
void LoadContacts();    // 파일 로드 함수 추가

#endif
// ch7_func.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ch7_func.h"

Contact contacts[MAX_CONTACTS];
int contactCount = 0;

void InsertContact() 
{
    if (contactCount >= MAX_CONTACTS)
    {
        printf("전화번호 목록이 가득 찼다.(100/100)\n\n");
        return;
    }

    printf("[INSERT]\n");
    printf("Input Name: ");
    fgets(contacts[contactCount].name, NAME_LENGTH, stdin);
    strtok(contacts[contactCount].name, "\n"); // 개행 문자 제거

    printf("Input Tel Number: ");
    fgets(contacts[contactCount].tel, TEL_LENGTH, stdin);
    strtok(contacts[contactCount].tel, "\n"); // 개행 문자 제거

    contactCount++;
    printf("\t\t\tData Inserted\n\n");
}

void DeleteContact() 
{
    char name[NAME_LENGTH];
    printf("[DELETE]\n");
    printf("Input Name that You Want to Delete: ");
    fgets(name, NAME_LENGTH, stdin);
    strtok(name, "\n"); // 개행 문자 제거

    for (int i = 0; i < contactCount; i++) 
    {
        if (strcmp(contacts[i].name, name) == 0) 
        {
            for (int j = i; j < contactCount - 1; j++) 
            {
                contacts[j] = contacts[j + 1]; // 데이터 이동
            }
            contactCount--;
            printf("\t\t\tData Deleted\n\n");
            return;
        }
    }
    printf("No such contact found with the name '%s'.\n\n", name);
}

void SearchContact() 
{
    char name[NAME_LENGTH];
    printf("[SEARCH]\n");
    printf("Input Name that You Want to Find: ");
    fgets(name, NAME_LENGTH, stdin);
    strtok(name, "\n"); // 개행 문자 제거

    for (int i = 0; i < contactCount; i++) 
    {
        if (strcmp(contacts[i].name, name) == 0) 
        {
            printf("[Data]\n");
            printf("Name: %s\n", contacts[i].name);
            printf("Tel: %s\n", contacts[i].tel);
            printf("\t\t\tData Searched\n\n");
            return;
        }
    }
    printf("No contact found with the name '%s'.\n\n", name);
}

void PrintAllContacts() 
{
    printf("[Print All Data]\n");
    for (int i = 0; i < contactCount; i++) 
    {
        printf("Name: %s\tTel: %s\n", contacts[i].name, contacts[i].tel);
    }
    printf("\t\t\tData Printed\n\n");
}

void SaveContacts() 
{
    FILE *file = fopen("phonebook.txt", "w");
    if (file == NULL) 
    {
        perror("Unable to open file for writing.\n");
        return;
    }
    for (int i = 0; i < contactCount; i++) 
    {
        fprintf(file, "%s\n%s\n", contacts[i].name, contacts[i].tel);
    }
    fclose(file);
    printf("Contacts saved to phonebook.txt\n\n");
}

void LoadContacts() 
{
    FILE *file = fopen("phonebook.txt", "r");
    if (file == NULL) 
    {
        printf("No existing contact data found. Starting fresh.\n");
        return;
    }
    while (fscanf(file, "%[^\n]\n%[^\n]\n", contacts[contactCount].name, contacts[contactCount].tel) == 2) 
    {
        contactCount++;
    }
    fclose(file);
    printf("Contacts loaded from phonebook.txt\n\n");
}
// ch7.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ch7_func.h"

int main() 
{
    int choice;

    // 데이터 로드
    LoadContacts();

    while (1) 
    {
        printf("*****MENU*****\n");
        printf("1. Insert\n");
        printf("2. Delete\n");
        printf("3. Search\n");
        printf("4. Print All\n");
        printf("5. Exit\n");
        printf("Choose the item: ");
        scanf("%d", &choice);
        getchar(); // 개행 문자 처리

        switch (choice) 
        {
            case 1:
                InsertContact();
                break;
            case 2:
                DeleteContact();
                break;
            case 3:
                SearchContact();
                break;
            case 4:
                PrintAllContacts();
                break;
            case 5:
                SaveContacts();  // 데이터 저장
                printf("Exiting...\n");
                return 0;
            default:
                printf("Invalid choice! Please try again.\n\n");
        }
    }
}
>명령어 & 출력
>gcc -o phonebook_new ch7_func.c ch7.c
>.\phonebook_new.exe
No existing contact data found. Starting fresh.
*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Kim
Input Tel Number: 111-2222
                        Data Inserted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 5
Contacts saved to phonebook.txt

Exiting...
>.\phonebook_new.exe
Contacts loaded from phonebook.txt

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Min
Input Tel Number: 010-2222-3333
                        Data Inserted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 3
[SEARCH]
Input Name that You Want to Find: Jung
No contact found with the name 'Jung'.

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 3
[SEARCH]
Input Name that You Want to Find: Kim
[Data]
Name: Kim
Tel: 111-2222
                        Data Searched

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 4
[Print All Data]
Name: Kim       Tel: 111-2222
Name: Min       Tel: 010-2222-3333
                        Data Printed

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 1
[INSERT]
Input Name: Jung
Input Tel Number: 010-1234-5678
                        Data Inserted

*****MENU*****
1. Insert
2. Delete
3. Search
4. Print All
5. Exit
Choose the item: 5
Contacts saved to phonebook.txt

Exiting...

-풀이-
전화번호 데이터를 다른 파일에 저장하고 불러오기 위해서는 저장하는 함수와 로드하는 함수를 따로 만들어 줘야했다.

  • 저장하는 함수

    void SaveContacts() 
    {
        FILE *file = fopen("phonebook.txt", "w");
        if (file == NULL) 
        {
            perror("Unable to open file for writing.\n");
            return;
        }
        for (int i = 0; i < contactCount; i++) 
        {
            fprintf(file, "%s\n%s\n", contacts[i].name, contacts[i].tel);
        }
        fclose(file);
        printf("Contacts saved to phonebook.txt\n\n");
    }
  • 로드하는 함수

    void LoadContacts() 
    {
        FILE *file = fopen("phonebook.txt", "r");
        if (file == NULL) 
        {
            printf("No existing contact data found. Starting fresh.\n");
            return;
        }
        while (fscanf(file, "%[^\n]\n%[^\n]\n", contacts[contactCount].name, contacts[contactCount].tel) == 2) 
        {
            contactCount++;
        }
        fclose(file);
        printf("Contacts loaded from phonebook.txt\n\n");
    }

이렇게 해서 만들어진 phonebook.txt 파일을 확인해보면

Kim
111-2222
Min
010-2222-3333
Jung
010-1234-5678

내용이 잘 들어가있는 것을 확인할 수 있다.


<Review>

Visual Studio Code에서 C언어를 하면서 컴파일할 때 한글이 제대로 컴파일 되지 않는 오류가 계속 발생해서 이에 대한 해결책을 찾아보았다. 해결 방안으로는 다음 4가지와 같다.

  1. 소스 파일 인코딩 확인
    소스 파일이 UTF-8로 인코딩되어 있는지 확인한다. Visual Studio Code에서 파일을 열고 오른쪽 아래의 인코딩 표시를 클릭하여 UTF-8로 변경한다.

  2. GCC 컴파일 시 인코딩 설정
    GCC에서 한글을 제대로 출력하도록 하기 위해, 컴파일할 때 다음과 같은 플래그를 추가할 수 있다.

    gcc -o example your_file.c -finput-charset=UTF-8 -fexec-charset=UTF-8
  3. 콘솔 인코딩 설정 (Windows)
    Windows 콘솔의 기본 인코딩이 UTF-8이 아닐 수 있다. 이를 해결하기 위해 아래 명령어를 사용하여 콘솔 인코딩을 UTF-8로 변경한다.

    chcp 65001

    이 명령어를 실행한 후 프로그램을 다시 실행해 보면 잘 되는 것을 확인할 수 있다.

  • stdio.h: 표준 입출력 함수(printf, fscanf, fopen, fclose 등)를 사용하기 위해 포함한다.
  • stdlib.h: 메모리 할당, 프로세스 제어, 변환 등의 함수(malloc, free, exit 등)를 사용하기 위해 포함한다.
  • string.h: 문자열 처리 함수(strlen, strcpy, strcat 등)를 사용하기 위해 포함한다.
  • ctype.h: 문자 변환 및 검사 함수를 사용하기 위해 포함한다.

마지막 6, 7번 문제는 쉬운듯 어려운듯 여태까지 배운 것들을 모두 사용할 수 있어서 재밌는 문제였다.

<참고 자료>

profile
백엔드 코린이😁

0개의 댓글