Add Final Project: Todo-App in C
This commit is contained in:
263
exercises/final_project/todo_c/todo.c
Normal file
263
exercises/final_project/todo_c/todo.c
Normal file
@@ -0,0 +1,263 @@
|
||||
// ============================================
|
||||
// Final-Projekt: Todo-App in C
|
||||
// Ein vollständiges CLI-Programm mit CRUD-Operationen
|
||||
// ============================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MAX_TODOS 100
|
||||
#define MAX_TITLE 100
|
||||
#define MAX_DESC 500
|
||||
#define DATA_FILE "todos.dat"
|
||||
|
||||
// Datenstruktur für ein Todo
|
||||
typedef struct {
|
||||
int id;
|
||||
char title[MAX_TITLE];
|
||||
char description[MAX_DESC];
|
||||
bool completed;
|
||||
time_t created_at;
|
||||
} Todo;
|
||||
|
||||
// Datenstruktur für die Todo-Liste
|
||||
typedef struct {
|
||||
Todo items[MAX_TODOS];
|
||||
int count;
|
||||
} TodoList;
|
||||
|
||||
// Funktionsprototypen
|
||||
void todo_init(TodoList* list);
|
||||
void todo_add(TodoList* list, const char* title, const char* description);
|
||||
void todo_list(const TodoList* list);
|
||||
void todo_complete(TodoList* list, int id);
|
||||
void todo_delete(TodoList* list, int id);
|
||||
void todo_save(const TodoList* list);
|
||||
void todo_load(TodoList* list);
|
||||
void print_menu(void);
|
||||
void clear_input_buffer(void);
|
||||
|
||||
// ============================================
|
||||
// IMPLEMENTATION
|
||||
// ============================================
|
||||
|
||||
void todo_init(TodoList* list) {
|
||||
list->count = 0;
|
||||
for (int i = 0; i < MAX_TODOS; i++) {
|
||||
list->items[i].id = 0;
|
||||
list->items[i].title[0] = '\0';
|
||||
list->items[i].description[0] = '\0';
|
||||
list->items[i].completed = false;
|
||||
list->items[i].created_at = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void todo_add(TodoList* list, const char* title, const char* description) {
|
||||
if (list->count >= MAX_TODOS) {
|
||||
printf("Fehler: Maximale Anzahl erreicht!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Todo* new_todo = &list->items[list->count];
|
||||
new_todo->id = list->count + 1;
|
||||
strncpy(new_todo->title, title, MAX_TITLE - 1);
|
||||
new_todo->title[MAX_TITLE - 1] = '\0';
|
||||
strncpy(new_todo->description, description, MAX_DESC - 1);
|
||||
new_todo->description[MAX_DESC - 1] = '\0';
|
||||
new_todo->completed = false;
|
||||
new_todo->created_at = time(NULL);
|
||||
|
||||
list->count++;
|
||||
printf("Todo #%d hinzugefügt!\n", new_todo->id);
|
||||
}
|
||||
|
||||
void todo_list(const TodoList* list) {
|
||||
if (list->count == 0) {
|
||||
printf("Keine Todos vorhanden.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n=== DEINE TODOS ===\n");
|
||||
printf("%-4s | %-10s | %-30s | %-20s\n", "ID", "Status", "Titel", "Erstellt");
|
||||
printf("-----+------------+--------------------------------+--------------------\n");
|
||||
|
||||
for (int i = 0; i < list->count; i++) {
|
||||
const Todo* todo = &list->items[i];
|
||||
char date_str[26];
|
||||
ctime_r(&todo->created_at, date_str);
|
||||
date_str[24] = '\0'; // Remove newline
|
||||
|
||||
const char* status = todo->completed ? "[✓] Done " : "[ ] Open ";
|
||||
printf("%-4d | %s | %-30s | %.20s\n",
|
||||
todo->id, status, todo->title, date_str);
|
||||
|
||||
if (strlen(todo->description) > 0) {
|
||||
printf(" | | %s\n", todo->description);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void todo_complete(TodoList* list, int id) {
|
||||
for (int i = 0; i < list->count; i++) {
|
||||
if (list->items[i].id == id) {
|
||||
list->items[i].completed = !list->items[i].completed;
|
||||
printf("Todo #%d %s!\n", id,
|
||||
list->items[i].completed ? "abgeschlossen" : "wieder geöffnet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("Fehler: Todo #%d nicht gefunden!\n", id);
|
||||
}
|
||||
|
||||
void todo_delete(TodoList* list, int id) {
|
||||
int found_index = -1;
|
||||
|
||||
for (int i = 0; i < list->count; i++) {
|
||||
if (list->items[i].id == id) {
|
||||
found_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_index == -1) {
|
||||
printf("Fehler: Todo #%d nicht gefunden!\n", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Array komprimieren
|
||||
for (int i = found_index; i < list->count - 1; i++) {
|
||||
list->items[i] = list->items[i + 1];
|
||||
list->items[i].id = i + 1; // ID neu zuweisen
|
||||
}
|
||||
|
||||
list->count--;
|
||||
printf("Todo #%d gelöscht!\n", id);
|
||||
}
|
||||
|
||||
void todo_save(const TodoList* list) {
|
||||
FILE* file = fopen(DATA_FILE, "wb");
|
||||
if (!file) {
|
||||
printf("Fehler: Konnte Datei nicht öffnen!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(&list->count, sizeof(int), 1, file);
|
||||
fwrite(list->items, sizeof(Todo), list->count, file);
|
||||
|
||||
fclose(file);
|
||||
printf("Todos gespeichert!\n");
|
||||
}
|
||||
|
||||
void todo_load(TodoList* list) {
|
||||
FILE* file = fopen(DATA_FILE, "rb");
|
||||
if (!file) {
|
||||
printf("Keine gespeicherte Daten gefunden. Starte neu...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fread(&list->count, sizeof(int), 1, file);
|
||||
fread(list->items, sizeof(Todo), list->count, file);
|
||||
|
||||
fclose(file);
|
||||
printf("%d Todos geladen!\n", list->count);
|
||||
}
|
||||
|
||||
void print_menu(void) {
|
||||
printf("\n=== TODO-APP ===\n");
|
||||
printf("1. Todo hinzufügen\n");
|
||||
printf("2. Todos anzeigen\n");
|
||||
printf("3. Todo abschließen/öffnen\n");
|
||||
printf("4. Todo löschen\n");
|
||||
printf("5. Speichern\n");
|
||||
printf("6. Beenden\n");
|
||||
printf("Wahl: ");
|
||||
}
|
||||
|
||||
void clear_input_buffer(void) {
|
||||
int c;
|
||||
while ((c = getchar()) != '\n' && c != EOF);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// HAUPTPROGRAMM
|
||||
// ============================================
|
||||
int main(void) {
|
||||
TodoList list;
|
||||
todo_init(&list);
|
||||
todo_load(&list);
|
||||
|
||||
int choice;
|
||||
char title[MAX_TITLE];
|
||||
char description[MAX_DESC];
|
||||
int id;
|
||||
|
||||
printf("\n📝 Willkommen zur Todo-App!\n");
|
||||
|
||||
while (true) {
|
||||
print_menu();
|
||||
|
||||
if (scanf("%d", &choice) != 1) {
|
||||
clear_input_buffer();
|
||||
printf("Ungültige Eingabe!\n");
|
||||
continue;
|
||||
}
|
||||
clear_input_buffer();
|
||||
|
||||
switch (choice) {
|
||||
case 1:
|
||||
printf("Titel: ");
|
||||
fgets(title, MAX_TITLE, stdin);
|
||||
title[strcspn(title, "\n")] = '\0'; // Remove newline
|
||||
|
||||
printf("Beschreibung (optional): ");
|
||||
fgets(description, MAX_DESC, stdin);
|
||||
description[strcspn(description, "\n")] = '\0';
|
||||
|
||||
todo_add(&list, title, description);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
todo_list(&list);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
printf("Todo-ID: ");
|
||||
if (scanf("%d", &id) == 1) {
|
||||
todo_complete(&list, id);
|
||||
}
|
||||
clear_input_buffer();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
printf("Todo-ID: ");
|
||||
if (scanf("%d", &id) == 1) {
|
||||
todo_delete(&list, id);
|
||||
}
|
||||
clear_input_buffer();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
todo_save(&list);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
printf("Speichern vor Beenden? (j/n): ");
|
||||
char save_choice;
|
||||
scanf(" %c", &save_choice);
|
||||
if (save_choice == 'j' || save_choice == 'J') {
|
||||
todo_save(&list);
|
||||
}
|
||||
printf("Auf Wiedersehen!\n");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
printf("Ungültige Wahl!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user