날짜

<aside> <img src="/icons/calendar_gray.svg" alt="/icons/calendar_gray.svg" width="40px" />

2025.02.13. 19 : 00 - 21 : 00

</aside>

계획

활동내용

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

#define DESCRIPTION_SIZE 200
#define FILENAME "tasks.txt"

// Task 구조체: 각 작업의 고유 id, 설명, 완료 상태를 저장합니다.
typedef struct Task {
    int id;
    char description[DESCRIPTION_SIZE];
    int isCompleted; // 0: 미완료, 1: 완료
} Task;

// 연결 리스트 노드 구조체: Task 데이터를 저장하고 다음 노드를 가리킵니다.
typedef struct Node {
    Task task;
    struct Node* next;
} Node;

Node* head = NULL;      // 연결 리스트의 시작 노드
int nextId = 1;         // 새 작업 추가 시 부여할 id

// 새 노드를 생성하는 함수
Node* createNode(Task t) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode != NULL) {
        newNode->task = t;
        newNode->next = NULL;
    }
    return newNode;
}

// 파일에서 작업 목록을 불러오는 함수
void loadTasks() {
    FILE* fp = fopen(FILENAME, "r");
    if (fp == NULL) {
        // 파일이 없으면 처음부터 시작
        return;
    }
    
    char line[512];
    while (fgets(line, sizeof(line), fp)) {
        // 줄바꿈 문자 제거
        line[strcspn(line, "\\n")] = '\\0';
        
        // 각 줄의 형식: id|isCompleted|description
        char* token = strtok(line, "|");
        if (token == NULL) continue;
        Task t;
        t.id = atoi(token);
        
        token = strtok(NULL, "|");
        if (token == NULL) continue;
        t.isCompleted = atoi(token);
        
        token = strtok(NULL, "|");
        if (token == NULL) continue;
        strncpy(t.description, token, DESCRIPTION_SIZE);
        t.description[DESCRIPTION_SIZE - 1] = '\\0';
        
        // 새 노드를 생성하여 리스트의 끝에 추가
        Node* newNode = createNode(t);
        if (newNode == NULL) continue;
        if (head == NULL) {
            head = newNode;
        } else {
            Node* current = head;
            while (current->next != NULL) {
                current = current->next;
            }
            current->next = newNode;
        }
        
        // 다음 id 업데이트: 불러온 작업들 중 최대 id보다 큰 값으로 설정
        if (t.id >= nextId) {
            nextId = t.id + 1;
        }
    }
    
    fclose(fp);
}

// 현재 작업 목록을 파일에 저장하는 함수
void saveTasks() {
    FILE* fp = fopen(FILENAME, "w");
    if (fp == NULL) {
        printf("파일 저장에 실패하였습니다.\\n");
        return;
    }
    
    Node* current = head;
    while (current != NULL) {
        fprintf(fp, "%d|%d|%s\\n", current->task.id, current->task.isCompleted, current->task.description);
        current = current->next;
    }
    
    fclose(fp);
    printf("작업 목록이 저장되었습니다.\\n");
}

// 작업 추가 함수: 사용자로부터 설명을 입력받아 새 작업을 추가합니다.
void addTask() {
    Task t;
    t.id = nextId++;
    t.isCompleted = 0;
    
    printf("새로운 작업의 설명을 입력하세요: ");
    // 버퍼에 남은 개행 문자를 제거
    int ch;
    while ((ch = getchar()) != '\\n' && ch != EOF);
    
    if (fgets(t.description, DESCRIPTION_SIZE, stdin) != NULL) {
        t.description[strcspn(t.description, "\\n")] = '\\0';
    }
    
    Node* newNode = createNode(t);
    if (newNode == NULL) {
        printf("메모리 할당에 실패하였습니다.\\n");
        return;
    }
    
    // 연결 리스트의 끝에 새 노드를 추가
    if (head == NULL) {
        head = newNode;
    } else {
        Node* current = head;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = newNode;
    }
    printf("작업이 추가되었습니다. (ID: %d)\\n", t.id);
}

// 작업 목록 출력 함수
void listTasks() {
    if (head == NULL) {
        printf("저장된 작업이 없습니다.\\n");
        return;
    }
    printf("\\n=== 작업 목록 ===\\n");
    Node* current = head;
    while (current != NULL) {
        printf("ID: %d | %s | [%s]\\n", 
            current->task.id, 
            current->task.description, 
            current->task.isCompleted ? "완료" : "미완료");
        current = current->next;
    }
}

// ID로 특정 작업을 찾는 함수
Node* findTaskById(int id) {
    Node* current = head;
    while (current != NULL) {
        if (current->task.id == id) {
            return current;
        }
        current = current->next;
    }
    return NULL;
}

// 작업 완료 처리 함수
void markTaskComplete() {
    int id;
    printf("완료 처리할 작업의 ID를 입력하세요: ");
    scanf("%d", &id);
    
    Node* target = findTaskById(id);
    if (target == NULL) {
        printf("해당 ID의 작업을 찾을 수 없습니다.\\n");
        return;
    }
    
    target->task.isCompleted = 1;
    printf("작업(ID: %d)이 완료 처리되었습니다.\\n", id);
}

// 작업 수정 함수: 작업 설명을 변경합니다.
void editTask() {
    int id;
    printf("수정할 작업의 ID를 입력하세요: ");
    scanf("%d", &id);
    
    Node* target = findTaskById(id);
    if (target == NULL) {
        printf("해당 ID의 작업을 찾을 수 없습니다.\\n");
        return;
    }
    
    // 버퍼 클리어
    int ch;
    while ((ch = getchar()) != '\\n' && ch != EOF);
    
    printf("새로운 작업 설명을 입력하세요: ");
    if (fgets(target->task.description, DESCRIPTION_SIZE, stdin) != NULL) {
        target->task.description[strcspn(target->task.description, "\\n")] = '\\0';
    }
    printf("작업(ID: %d)이 수정되었습니다.\\n", id);
}

// 작업 삭제 함수: 지정된 ID의 작업을 삭제합니다.
void deleteTask() {
    int id;
    printf("삭제할 작업의 ID를 입력하세요: ");
    scanf("%d", &id);
    
    Node *current = head, *prev = NULL;
    while (current != NULL && current->task.id != id) {
        prev = current;
        current = current->next;
    }
    
    if (current == NULL) {
        printf("해당 ID의 작업을 찾을 수 없습니다.\\n");
        return;
    }
    
    if (prev == NULL) {
        head = current->next;
    } else {
        prev->next = current->next;
    }
    
    free(current);
    printf("작업(ID: %d)이 삭제되었습니다.\\n", id);
}

// 작업 검색 함수: 입력한 키워드를 설명에서 검색합니다.
void searchTasks() {
    char keyword[50];
    // 버퍼 클리어
    int ch;
    while ((ch = getchar()) != '\\n' && ch != EOF);
    
    printf("검색할 키워드를 입력하세요: ");
    if (fgets(keyword, sizeof(keyword), stdin) != NULL) {
        keyword[strcspn(keyword, "\\n")] = '\\0';
    }
    
    int found = 0;
    Node* current = head;
    printf("\\n=== 검색 결과 ===\\n");
    while (current != NULL) {
        if (strstr(current->task.description, keyword) != NULL) {
            printf("ID: %d | %s | [%s]\\n", 
                current->task.id, 
                current->task.description, 
                current->task.isCompleted ? "완료" : "미완료");
            found = 1;
        }
        current = current->next;
    }
    
    if (!found) {
        printf("키워드 '%s'와 일치하는 작업이 없습니다.\\n", keyword);
    }
}

// 연결 리스트의 메모리를 해제하는 함수
void freeList() {
    Node* current = head;
    while (current != NULL) {
        Node* temp = current;
        current = current->next;
        free(temp);
    }
}

int main() {
    int choice;
    loadTasks();
    
    while (1) {
        printf("\\n--- 할 일 관리 프로그램 ---\\n");
        printf("1. 작업 추가\\n");
        printf("2. 작업 목록 보기\\n");
        printf("3. 작업 완료 처리\\n");
        printf("4. 작업 수정\\n");
        printf("5. 작업 삭제\\n");
        printf("6. 작업 검색\\n");
        printf("7. 저장 후 종료\\n");
        printf("메뉴 선택: ");
        scanf("%d", &choice);
        
        switch (choice) {
            case 1:
                addTask();
                break;
            case 2:
                listTasks();
                break;
            case 3:
                markTaskComplete();
                break;
            case 4:
                editTask();
                break;
            case 5:
                deleteTask();
                break;
            case 6:
                searchTasks();
                break;
            case 7:
                saveTasks();
                freeList();
                printf("프로그램을 종료합니다.\\n");
                exit(0);
            default:
                printf("잘못된 선택입니다. 다시 시도하세요.\\n");
        }
    }
    
    return 0;
}