Einführung
Eine Pause hat ein Zeitlimit. Deine Musikbibliothek enthält vollständige Aufnahmen mit einer Präferenzbewertung, einer Dauer in Sekunden und einer stabilen Track-ID. Du entwickelst ein wiederverwendbares C++-Programm, das die Titel in einer festgelegten Reihenfolge betrachtet, jede passende Aufnahme übernimmt und eine tatsächlich abspielbare Liste speichert.
Du brauchst grundlegende Terminalnavigation sowie Kenntnisse über C++-Strings, Klassen, vector, Dateistreams und Ausnahmen. Die hier verwendeten const-Referenzen, die Vergleichsfunktion und relative Pfade werden erklärt. Parser, eigene synthetische Musik und Browserplayer sind vorbereitet. Du schreibst Auswahl und Export, keinen Audiokonverter und keinen Algorithmus für eine optimale Packung.
Verwende den Tab Terminal für Befehle und Player zum Anhören. Beide nutzen dieselbe Umgebung. Bearbeite nur selection.cpp und export.cpp und erstelle Listen direkt in outputs/. Katalog, bereitgestelltes Programmgerüst und Originale unter outputs/audio/ bleiben unverändert. Die eigens erstellten synthetischen Kompositionen enthalten keine fremden Audiosamples.
Vollständige Titel in die Pause einpassen
In diesem Schritt hörst du zuerst ein normales Beispiel und wählst anschließend vollständige Aufnahmen mit einem Zähler für die verbleibende Zeit aus. Beginne in Player. Wähle example-120.m3u8, klicke auf Load playlist und dann auf Play 1. Die Liste enthält Morning Light (50 Sekunden) und Window Seat (70 Sekunden). Mit den Audiobedienelementen kannst du pausieren oder die Wiedergabeposition verschieben. Wähle danach example-90.m3u8, lade die Liste und spiele ihren einzigen Titel ab. Die kürzere Pause behält die erste vollständige Aufnahme bei, statt die zweite zu kürzen.
Wechsle zu Terminal. Falls fremde Zeichen oder ein unfertiger Befehl an der Eingabeaufforderung stehen, brich diese Eingabe mit Ctrl+C ab. cd wechselt das Verzeichnis. Dieser absolute Pfad hilft auch bei einem gewöhnlichen Fehler im Arbeitsverzeichnis:
cd /home/labex/project/short-break
cat zeigt eine Textdatei an. sample.tsv ist ein Katalog mit zwei Titeln; TSV bedeutet durch Tabulatoren getrennte Werte. Die Spalten enthalten ID, Titel, ganze Sekunden, Präferenz und Audiopfad. Höhere Präferenzen werden zuerst betrachtet. Eine ID bezeichnet eine Aufnahme; Titel dürfen mehrfach vorkommen.
cat sample.tsv
playlist.hpp deklariert das Objekt Track und die Schnittstellen der beiden Funktionen. Ein Track bündelt die fünf Felder. Ein vector<Track> speichert mehrere Objekte in einer Reihenfolge. Lies die kurze Definition:
cat playlist.hpp
Kompiliere das vorbereitete Startprogramm. g++ übersetzt C++; -std=c++17 wählt C++17, -Wall -Wextra aktiviert hilfreiche Warnungen und -o playlist benennt die ausführbare Datei. Die drei .cpp-Dateien werden zusammen kompiliert. Eine Warnung zum ungenutzten Budget im Startprogramm ist zu erwarten.
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist startet die ausführbare Datei im aktuellen Verzeichnis. Die Argumente sind Katalog, Zeitbudget in ganzen Sekunden von 1 bis einschließlich 86400 und Ausgabepfad. Der vorbereitete Parser verwendet dieselben Grenzen für Dauer und Präferenz im Katalog; du musst ihn nicht implementieren. Führe die unvollständige Version mit dem 90-Sekunden-Beispiel aus:
./playlist sample.tsv 90 outputs/sample-90.m3u8
Sie gibt beide Aufnahmen und Total: 120s / 90s aus. Diesen Fehler wirst du beheben. Die Ausgabedatei enthält bisher nur Titel. Beim Laden in Player wird deshalb der fehlende Kopf #EXTM3U gemeldet. Das ist ein erwarteter Formatfehler und unterscheidet sich von einer fehlenden Audiodatei. Schritt 3 erklärt das Exportformat.
Gib chooseTracks nun ein verbleibendes Budget. Die Übergabe von tracks als Wert erzeugt eine lokale Kopie; späteres Sortieren ändert den Katalog nicht. const auto& track liest das vorhandene Objekt, ohne es zu kopieren oder zu verändern. push_back hängt ein ausgewähltes Objekt an. <= berücksichtigt auch einen genau passenden Titel. Danach zieht -= dessen Dauer ab. Ein zu langer Titel wird übersprungen, und die Schleife läuft weiter.
Ersetze die kurze Datei durch die vollständige Funktion unten. > ersetzt diese Quelldatei. Das Here-Dokument <<'CPP' übergibt alle Zeilen bis zum abschließenden CPP an cat; der zitierte Begrenzer erhält die C++-Zeichen unverändert. Füge den gesamten Block ein, einschließlich des abschließenden Begrenzers auf einer eigenen Zeile.
cat > selection.cpp <<'CPP'
#include "playlist.hpp"
#include <algorithm>
std::vector<Track> chooseTracks(std::vector<Track> tracks, int budget) {
std::vector<Track> selected;
for (const auto& track : tracks) {
if (track.seconds <= budget) {
selected.push_back(track);
budget -= track.seconds;
}
}
return selected;
}
CPP
Kompiliere nach der Änderung erneut und starte dasselbe Beispiel:
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist sample.tsv 90 outputs/sample-90.m3u8
Jetzt erscheinen nur t01 | Morning Light | 50s und Total: 50s / 90s. Vierzig Sekunden bleiben übrig; die 70-Sekunden-Aufnahme passt nicht. Diese Version hält sich an die Eingabereihenfolge. Als Nächstes machst du die Reihenfolge unabhängig von den Zeilen des Katalogs.
Falls ein Compilerfehler eine Zeile nennt, öffne selection.cpp im Editor oder wiederhole den vollständigen Ersetzungsblock und kompiliere erneut. Eine alte ausführbare Datei nach einem fehlgeschlagenen Build zu starten, prüft deine Änderung nicht.
Eine eindeutige Präferenzreihenfolge verwenden
In diesem Schritt sortierst du die gesamte Bibliothek vor der Auswahl. Beginne in Terminal im Verzeichnis /home/labex/project/short-break. Sieh dir den vollständigen Katalog an:
cat catalog.tsv
Die Zeilen sind absichtlich gemischt. Betrachte Präferenzen absteigend und bei Gleichstand die Track-ID alphabetisch aufsteigend. Daher kommt t02 vor t03, obwohl die Aufnahme länger ist. Das sind einfache Präferenzentscheidungen, keine Suche nach der größtmöglichen Gesamtdauer.
Bei 90 Sekunden Budget verbraucht t01 bereits 50. Weder t02 (95) noch t03 (70) passen in die restlichen 40. Fahre mit t04 (30) fort; danach bleiben 10 Sekunden. t05 (20) passt nicht. Das Ergebnis lautet t01, t04 mit insgesamt 80 Sekunden. Ein Abbruch beim ersten zu langen Titel würde eine spätere gültige Auswahl verhindern.
std::sort verwendet den halboffenen Bereich von begin() bis end(). Der Vergleich liefert true, wenn a vor b stehen soll. [] beginnt eine kleine anonyme Funktion, einen Lambda-Ausdruck; die beiden Parameter const Track& lesen die verglichenen Objekte. != entscheidet über den primären Vergleich, und das letzte return löst einen Gleichstand über die ID. Ersetze selection.cpp durch die vollständige Version:
cat > selection.cpp <<'CPP'
#include "playlist.hpp"
#include <algorithm>
std::vector<Track> chooseTracks(std::vector<Track> tracks, int budget) {
std::sort(tracks.begin(), tracks.end(),
[](const Track& a, const Track& b) {
if (a.preference != b.preference)
return a.preference > b.preference;
return a.id < b.id;
});
std::vector<Track> selected;
for (const auto& track : tracks) {
if (track.seconds <= budget) {
selected.push_back(track);
budget -= track.seconds;
}
}
return selected;
}
CPP
Kompiliere erneut und führe beide Budgets aus:
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist catalog.tsv 90 outputs/break-90.m3u8
./playlist catalog.tsv 120 outputs/break-120.m3u8
Für 90 Sekunden erhältst du t01, t04 mit insgesamt 80. Für 120 Sekunden erhältst du t01, t03, genau 120: t02 wird übersprungen, dann passt der 70-Sekunden-Titel genau in die verbleibenden 70. Beide Auswahlen nutzen vollständige Aufnahmen. Bis zum nächsten Schritt bleibt die Ausgabe eine reine Titelliste.
Eine abspielbare Liste exportieren
In diesem Schritt speicherst du die gewählten Objekte als erweitertes M3U in UTF-8. Beginne in Terminal. Eine .m3u8-Datei ist eine Textliste und enthält nicht selbst das Audio. Die erste Zeile lautet #EXTM3U. Für jeden ausgewählten Titel folgt eine Metadatenzeile #EXTINF:seconds,title und anschließend sein Audiopfad.
Ein relativer Pfad wird vom Verzeichnis der Wiedergabeliste aus aufgelöst, nicht vom Katalogverzeichnis oder aktuellen Verzeichnis des Prozesses. Speichere erzeugte Listen direkt in outputs/; die Originale liegen in outputs/audio/. Ein Eintrag wie audio/Morning Light.wav erreicht daher die Aufnahme neben der Liste. Behalte diese Ordnerbeziehung beim Herunterladen oder Teilen bei. Leerzeichen und Unicode werden unterstützt. Verwende die vorhandenen Pfade ohne Zeilenumbrüche, führendes #, absolute Pfade, Wechsel ins übergeordnete Verzeichnis oder zusätzliche Playlist-Anweisungen.
std::ofstream öffnet die Ausgabedatei. << schreibt Text oder Zahlen; \n schreibt einen Zeilenumbruch. Die Funktion erhält den ausgewählten vector und den Ausgabenamen über const-Referenzen, sodass sie diese nicht kopiert oder verändert. Dateifehler lösen eine Ausnahme aus, die das vorbereitete Hauptprogramm meldet. Der Parser prüft positives ganzzahliges Budget, Katalog und sämtliche referenzierten Audiodateien, bevor diese Funktion die Ausgabe öffnet. Bei diesen Eingabefehlern bleibt eine vorhandene Ausgabe erhalten.
Ersetze export.cpp:
cat > export.cpp <<'CPP'
#include "playlist.hpp"
#include <fstream>
#include <stdexcept>
void writePlaylist(const std::vector<Track>& tracks, const std::string& output) {
std::ofstream file(output);
if (!file) throw std::runtime_error("Cannot write playlist.");
file << "#EXTM3U\n";
for (const auto& track : tracks) {
file << "#EXTINF:" << track.seconds << ',' << track.title << '\n';
file << track.path << '\n';
}
if (!file) throw std::runtime_error("Playlist write failed.");
}
CPP
Kompiliere erneut und erzeuge beide Listen mit dem aktualisierten Exporter:
g++ -std=c++17 -Wall -Wextra main.cpp selection.cpp export.cpp -o playlist
./playlist catalog.tsv 90 outputs/break-90.m3u8
./playlist catalog.tsv 120 outputs/break-120.m3u8
Untersuche die gespeicherte 90-Sekunden-Datei:
cat outputs/break-90.m3u8
Die gespeicherte UTF-8-Datei enthält:
#EXTM3U
#EXTINF:50,Morning Light
audio/Morning Light.wav
#EXTINF:30,夜の散歩
audio/夜の散歩.wav
Der japanische Titel und Dateiname bleiben UTF-8-Text. Manche Terminals zeigen Ersatzzeichen an, obwohl die gespeicherten Bytes intakt sind. Benenne die Originale deshalb nicht um; Player zeigt Originaltitel und Originalpfad. Die Dauerzeilen beschreiben die vollständigen Dateien. Eine andere Zahl an dieser Stelle kürzt das Audio nicht.
Wechsle zu Player. Klicke auf Refresh files, wähle break-90.m3u8 und klicke auf Load playlist. Es sollten zwei Titel und 80 deklarierte Sekunden erscheinen. Klicke auf Play 1, verschiebe die Wiedergabeposition und pausiere mit den Audiobedienelementen. Klicke dann auf Play 2, um die zweite Aufnahme zu hören. Der Browser liest deine gespeicherte M3U8-Datei und genau die darin angegebenen Pfade. Eine fehlende Quelle führt zu Cannot play, nicht zu einer Ersatzaufnahme. Mit Download playlist und Download current track speicherst du die tatsächlichen Dateien. Die Liste allein enthält das Audio nicht.

Achte auf die zwei Zeilen mit ihren Dauern und die Audiobedienelemente darunter. Wechsle nach der Wiedergabeprüfung zurück zu Terminal.
Das Programm wiederverwenden und eine Ablehnung verstehen
In diesem Schritt unterscheidest du ein gültiges kurzes Ergebnis von ungültigen Eingaben. Beginne in Terminal. Ein positives Budget, das kürzer als jeder Titel ist, ist gültig: Keine vollständige Aufnahme passt hinein. Erzeuge eine Liste für eine Sekunde:
./playlist catalog.tsv 1 outputs/tiny.m3u8
Das Programm meldet Total: 0s / 1s. Die Datei enthält nur #EXTM3U. Beim Laden in Player sollte sie null Titel anzeigen. Das ist ein korrektes leeres Ergebnis und kein Verbindungs- oder Parserfehler.
Ein Budget wie 90s ist ungültig, weil die CLI Ziffern für ganze Sekunden erwartet. Der nächste Befehl endet absichtlich mit Status 1 und darf deine vorhandene 90-Sekunden-Liste nicht ersetzen. echo $? zeigt unmittelbar den Exitstatus des vorigen Befehls (0 bedeutet Erfolg). Führe diese zwei Befehle direkt nacheinander aus:
./playlist catalog.tsv 90s outputs/break-90.m3u8
echo $?
Die Fehlermeldung erklärt, dass Budget und Dauer positive ganze Sekunden sein müssen; danach erscheint Status 1. Setze die Umgebung nicht zurück. Entferne das Suffix und verwende beim nächsten Versuch 90. Bei einem falsch geschriebenen Eingabepfad verweist Cannot open catalog auf das Katalogargument. Korrigiere mit dem absoluten cd aus Schritt 1 und dem tatsächlichen Namen catalog.tsv. Missing audio bedeutet, dass ein Katalogpfad keine Datei erreicht. Erhalte die vorbereiteten Originale und prüfe den Pfad vor einem neuen Versuch.
Prüfe, dass die vorhandene korrekte Datei weiterhin beide richtigen Metadaten-/Pfadpaare enthält:
cat outputs/break-90.m3u8
Du kannst dasselbe Programm nun für unterschiedliche gültige positive Budgets verwenden und immer direkt in outputs/ schreiben. Es sortiert nach Präferenz und ID, überspringt zu lange Kandidaten und kürzt niemals eine Aufnahme. Physische Lautsprecherausgabe und das Verhalten aller möglichen Drittanbieterplayer liegen außerhalb dieser Übung. Der bereitgestellte Browser decodiert diese echten WAV-Dateien.
Zusammenfassung
Du hast C++-Objekte und einen vector verwendet, um Aufnahmen zu ordnen, vollständige Titel innerhalb eines verbleibenden Budgets auszuwählen und UTF-8-M3U8-Dateien zu schreiben. Du hast die echte Ausgabe abgespielt, relative Pfade verstanden und eine gültige leere Liste von einer ungültigen Eingabe unterschieden. Die folgende Challenge ändert die Bibliothek, behält aber diese Regeln bei.



