<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;
}