Add Final Project: Todo-App in C++

This commit is contained in:
2026-04-15 00:18:56 +02:00
parent 01790a0f39
commit b31af8ebb8

View File

@@ -0,0 +1,332 @@
// ============================================
// Final-Projekt: Todo-App in C++
// Moderne C++17 Implementierung mit STL
// ============================================
#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <algorithm>
// ============================================
// KLASSE: Todo
// ============================================
class Todo {
private:
int id_;
std::string title_;
std::string description_;
bool completed_;
std::time_t created_at_;
public:
// Konstruktor
Todo(int id, std::string title, std::string description)
: id_(id)
, title_(std::move(title))
, description_(std::move(description))
, completed_(false)
, created_at_(std::time(nullptr)) {}
// Getter
int id() const { return id_; }
const std::string& title() const { return title_; }
const std::string& description() const { return description_; }
bool completed() const { return completed_; }
std::time_t created_at() const { return created_at_; }
// Setter
void toggle_completed() { completed_ = !completed_; }
void set_title(const std::string& title) { title_ = title; }
void set_description(const std::string& desc) { description_ = desc; }
// Formatierung für Ausgabe
std::string to_string() const {
std::stringstream ss;
ss << "#" << id_ << " [" << (completed_ ? "" : " ") << "] " << title_;
return ss.str();
}
// Serialisierung für Datei-Speicherung
std::string serialize() const {
std::stringstream ss;
ss << id_ << "|" << title_ << "|" << description_ << "|"
<< (completed_ ? "1" : "0") << "|" << created_at_;
return ss.str();
}
static Todo deserialize(const std::string& line) {
std::stringstream ss(line);
std::string part;
std::vector<std::string> parts;
while (std::getline(ss, part, '|')) {
parts.push_back(part);
}
Todo todo(std::stoi(parts[0]), parts[1], parts[2]);
if (parts[3] == "1") todo.toggle_completed();
return todo;
}
};
// ============================================
// KLASSE: TodoManager
// ============================================
class TodoManager {
private:
std::vector<Todo> todos_;
int next_id_ = 1;
const std::string filename_ = "todos_cpp.txt";
public:
// CRUD Operationen
void add(const std::string& title, const std::string& description) {
todos_.emplace_back(next_id_++, title, description);
std::cout << "✓ Todo hinzugefügt!\n";
}
void list() const {
if (todos_.empty()) {
std::cout << "Keine Todos vorhanden.\n";
return;
}
std::cout << "\n=== DEINE TODOS ===\n";
for (const auto& todo : todos_) {
std::cout << todo.to_string() << "\n";
if (!todo.description().empty()) {
std::cout << " " << todo.description() << "\n";
}
}
std::cout << "\n";
}
void complete(int id) {
auto it = find_by_id(id);
if (it != todos_.end()) {
it->toggle_completed();
std::cout << "✓ Todo #" << id << (it->completed() ? " abgeschlossen" : " wieder geöffnet") << "\n";
} else {
std::cout << "✗ Todo #" << id << " nicht gefunden!\n";
}
}
void remove(int id) {
auto it = find_by_id(id);
if (it != todos_.end()) {
todos_.erase(it);
// IDs neu zuweisen
for (size_t i = 0; i < todos_.size(); ++i) {
// Note: In echtem Code würde man IDs nicht ändern
}
std::cout << "✓ Todo #" << id << " gelöscht!\n";
} else {
std::cout << "✗ Todo #" << id << " nicht gefunden!\n";
}
}
void edit(int id, const std::string& new_title) {
auto it = find_by_id(id);
if (it != todos_.end()) {
it->set_title(new_title);
std::cout << "✓ Todo #" << id << " aktualisiert!\n";
} else {
std::cout << "✗ Todo #" << id << " nicht gefunden!\n";
}
}
// Filter-Funktionen
void list_open() const {
std::cout << "\n=== OFFENE TODOS ===\n";
for (const auto& todo : todos_) {
if (!todo.completed()) {
std::cout << todo.to_string() << "\n";
}
}
std::cout << "\n";
}
void list_completed() const {
std::cout << "\n=== ABGESCHLOSSENE TODOS ===\n";
for (const auto& todo : todos_) {
if (todo.completed()) {
std::cout << todo.to_string() << "\n";
}
}
std::cout << "\n";
}
void search(const std::string& keyword) const {
std::cout << "\n=== SUCHERGEBNISSE ===\n";
for (const auto& todo : todos_) {
if (todo.title().find(keyword) != std::string::npos ||
todo.description().find(keyword) != std::string::npos) {
std::cout << todo.to_string() << "\n";
}
}
std::cout << "\n";
}
// Datei-Operationen
void save() const {
std::ofstream file(filename_);
if (!file) {
std::cout << "✗ Fehler beim Speichern!\n";
return;
}
for (const auto& todo : todos_) {
file << todo.serialize() << "\n";
}
std::cout << "" << todos_.size() << " Todos gespeichert!\n";
}
void load() {
std::ifstream file(filename_);
if (!file) {
std::cout << "Keine vorhandenen Daten. Starte neu...\n";
return;
}
todos_.clear();
std::string line;
while (std::getline(file, line)) {
if (!line.empty()) {
todos_.push_back(Todo::deserialize(line));
}
}
// next_id aktualisieren
if (!todos_.empty()) {
next_id_ = std::max_element(todos_.begin(), todos_.end(),
[](const Todo& a, const Todo& b) { return a.id() < b.id(); })->id() + 1;
}
std::cout << "" << todos_.size() << " Todos geladen!\n";
}
private:
std::vector<Todo>::iterator find_by_id(int id) {
return std::find_if(todos_.begin(), todos_.end(),
[id](const Todo& t) { return t.id() == id; });
}
};
// ============================================
// UI-FUNKTIONEN
// ============================================
void print_menu() {
std::cout << "\n=== TODO-APP (C++ Edition) ===\n";
std::cout << "1. Todo hinzufügen\n";
std::cout << "2. Alle Todos anzeigen\n";
std::cout << "3. Offene Todos anzeigen\n";
std::cout << "4. Abgeschlossene Todos anzeigen\n";
std::cout << "5. Todo abschließen/öffnen\n";
std::cout << "6. Todo bearbeiten\n";
std::cout << "7. Todo löschen\n";
std::cout << "8. Suchen\n";
std::cout << "9. Speichern\n";
std::cout << "0. Beenden\n";
std::cout << "Wahl: ";
}
// ============================================
// HAUPTPROGRAMM
// ============================================
int main() {
TodoManager manager;
manager.load();
std::cout << "\n📝 Willkommen zur Todo-App (C++17)!\n";
int choice;
std::string title, description, keyword;
int id;
while (true) {
print_menu();
if (!(std::cin >> choice)) {
std::cin.clear();
std::cin.ignore(10000, '\n');
std::cout << "Ungültige Eingabe!\n";
continue;
}
std::cin.ignore(10000, '\n'); // Rest der Zeile ignorieren
switch (choice) {
case 1:
std::cout << "Titel: ";
std::getline(std::cin, title);
std::cout << "Beschreibung: ";
std::getline(std::cin, description);
manager.add(title, description);
break;
case 2:
manager.list();
break;
case 3:
manager.list_open();
break;
case 4:
manager.list_completed();
break;
case 5:
std::cout << "Todo-ID: ";
std::cin >> id;
manager.complete(id);
break;
case 6:
std::cout << "Todo-ID: ";
std::cin >> id;
std::cin.ignore(10000, '\n');
std::cout << "Neuer Titel: ";
std::getline(std::cin, title);
manager.edit(id, title);
break;
case 7:
std::cout << "Todo-ID: ";
std::cin >> id;
manager.remove(id);
break;
case 8:
std::cout << "Suchbegriff: ";
std::getline(std::cin, keyword);
manager.search(keyword);
break;
case 9:
manager.save();
break;
case 0: {
std::cout << "Speichern vor Beenden? (j/n): ";
char save_choice;
std::cin >> save_choice;
if (save_choice == 'j' || save_choice == 'J') {
manager.save();
}
std::cout << "Auf Wiedersehen!\n";
return 0;
}
default:
std::cout << "Ungültige Wahl!\n";
}
}
return 0;
}