assignments/Assignment_009/readme.md

7.8 KiB
Raw Permalink Blame History

Mist die Festplatte ist schon wieder voll

🎓 Benotetes Assignment 🎓

📆 Fällig: 02.12.2025 📆

Nachdem Sie den Reptiloiden "Never gonna give you up" vorgesungen haben und ein Bild von Rick Astley entgegen gehalten, sind diese wie zu Stein erstarrt und Sie können aus der Höhle flüchten. Hinter sich hören Sie noch das Grunzen und Schreien der Echsenwesen und glauben auch die Stimme von Mark Zuckerberg zu vernehmen. Nie wieder Facebook, Instagram und WhatsApp das war einfach zu viel.

Geschafft! Sie sind an der Erdoberfläche angekommen und schlagen sich nach Hause durch. Die Reise dauert durchaus eine Weile, auch wegen der ständig verspäteten Züge, und als Sie ankommen, brauchen Sie erst einmal etwas Entspannung.

Was kann mehr entspannen als ... eine Runde Battlefield 6.

Doch leider ist die Festplatte voll und der 67 GB Download passt nicht mehr drauf. Sie müssen dringend Platz schaffen! Also suchen Sie nach Möglichkeiten, die Daten auf Ihrer Festplatte, die eigentlich eine SSD ist, zu komprimieren.

Da kommt Ihnen die nächste PR2-Aufgabe gerade gelegen.

CompressingOutputStream

Gehen Sie in das Paket pr2.io.compressor.

Implementieren Sie eine Klasse namens CompressingOutputStream, die von FilterOutputStream abgeleitet ist. Diese Klasse soll eine Komprimierung der Daten durchführen, wobei ein sogenanntes run length encoding verwendet werden soll. Immer wenn ein Byte zwei- oder mehrmals hintereinander vorkommt, soll statt der wiederholten Bytes, die Sequenz -1, das Byte und die Anzahl der Wiederholungen geschrieben werden. So wird z.B. aus der Bytefolge { 5, 5, 5, 5, 5, 5, 5, 5, 5 } durch die Kompression die Folge { -1, 5, 9 }. Wiederholt sich ein Byte nicht, wird es einfach direkt übernommen. Es sind maximal 126 Wiederholungen möglich. Kommt ein Byte häufiger als 126-mal hintereinander vor, werden zuerst die ersten 126 Byte komprimiert und dann die nächsten Bytes bearbeitet. (Die Bytewerte sind hier als Dezimalzahlen angegeben, -1 würde dann in hexadezimaler Darstellung 0xff entsprechen.)

Beachten Sie den Spezialfall, dass die Eingabedaten eine -1 (0xff) enthalten, in diesem Fall müssen Sie die Escapesequenz { -1, -1 } schreiben, um dies bei der Dekompression erkennen zu können. So wird z.B. aus der Eingabe { 3, -1, 5 } komprimiert { 3, -1, -1, 5 }.

Testen Sie Ihre Implementierung ausführlich mit Unit-Tests. Sie können folgende Bytefolgen als Startpunkt nehmen, sie sind aber auf keinen Fall ausreichend.

Input Komprimiert
0, 1, 2, 3 0, 1, 2, 3
-1, -1, -1 -1, -1, -1, -1, -1, -1
-1, 1, 2, 3 -1, -1, 1, 2, 3
0, 1, 1, 1, 1, 1, 1 0, -1, 1, 6
5, 5, 5, 5, 5, 5, 5, 5, 5 -1, 5, 9
0, 1, 1, 3, 3, 3, 3 0, -1, 1, 2, -1, 3, 4
1, 1, 3, 3, 3, 3 -1, 1, 2, -1, 3, 4
1, 2, 1, 3, 3, 3 1, 2, 1, -1, 3, 3
1, 1, -1, 1, 2, 3 -1, 1, 2, -1, -1, 1, 2, 3
1, 1, -1 -1, 1, 2, -1, -1
-1 -1, -1
-1, 1, 1 -1, -1, -1, 1, 2
1, 1, 1, 1, 1, 1, 1, 1, 1 -1, 1, 9
1, 1, 1, 1, -1 -1, 1, 4, -1, -1

DecompressingInputStream

Implementieren Sie eine Klasse namens DecompressingInputStream, die von InputStream abgeleitet ist. Diese Klasse soll eine Dekomprimierung der Daten durchführen, die vorher mit einem CompressingOutputStream komprimiert wurden.

Testen Sie Ihre Implementierung ausführlich mit Unit-Tests. Sie können die in der obigen Tabelle dargestellten Bytefolgen als Startpunkt nehmen, sollten aber noch mehr Fälle testen.

FileCompressor

Implementieren Sie eine Klasse namens FileCompressor. Diese nimmt von der Kommandozeile zwei Argumente an, die zwei Dateien bezeichnen. Die zuerst angegebene Datei wird mithilfe eines CompressingOutputStream komprimiert und das Ergebnis wird in die als zweite Option angegebene Datei geschrieben. Denken Sie an eine sinnvolle Fehlerbehandlung.

Alternativ kann man die Klasse auch ohne Optionen aufrufen. In diesem Fall werden die Daten von der Standard-Eingabe gelesen und das komprimierte Ergebnis auf die Standard-Ausgabe geschrieben.

Diese letzte Anforderung bedeutet, dass man z.B. das folgende machen kann:

$ echo "Duuuuuuuuuuuuduuuuuuuuuu" | \
    java -cp . pr2.io.compressor.tool.FileCompressor > \
    result.rle

FileDecompressor

Implementieren Sie eine Klasse namens FileDecompressor. Diese nimmt von der Kommandozeile zwei Argumente an, die zwei Dateien bezeichnen. Die zuerst angegebene Datei wird mithilfe eines DecompressingInputStreams dekomprimiert und das Ergebnis wird in die als zweite Option angegebene Datei geschrieben. Denken Sie an eine sinnvolle Fehlerbehandlung.

Alternativ kann man die Klasse auch ohne Optionen aufrufen. In diesem Fall werden die Daten von der Standard-Eingabe gelesen und das dekomprimierte Ergebnis auf die Standard-Ausgabe geschrieben.

Diese letzte Anforderung bedeutet, dass man z.B. das folgende machen kann:

$ echo "Duuuuuuuuuuuuduuuuuuuuuu" | \
     java -cp . pr2.io.compressor.tool.FileCompressor | \
     java -cp . pr2.io.compressor.tool.FileDecompressor
Duuuuuuuuuuuuduuuuuuuuuu

DirectoryCompressor

Schreiben Sie eine Klasse namens DirectoryCompressor. Diese nimmt von der Kommandozeile den Namen eines Verzeichnisses entgegen und komprimiert alle darin gefundenen Dateien mit dem oben beschriebenen FileCompressor. Die komprimierten Dateien werden mit der Dateierweiterung .rle versehen, z.B. wird aus der Datei MyFile.txt die Datei MyFile.txt.rle. Die Originaldateien werden nicht verändert.

Beachten Sie die folgenden möglichen Fehlerfälle:

  • Der Benutzer vergisst, einen Pfad anzugeben.
  • Das angegebene Verzeichnis existiert nicht.
  • Der angegebene Pfad ist gar kein Verzeichnis.
  • Das Verzeichnis existiert, darf aber nicht gelesen werden.
  • Das Verzeichnis existiert, darf aber nicht geschrieben werden.

Darüber hinaus können noch andere Fehler auftreten, die Sie bitte entsprechend abfangen und behandeln sollen.

Das Programm soll den Benutzer über seine Fortschritte informieren. Hierzu soll die Ausgabe ungefähr wie folgt aussehen:

Komprimiere Dateien in /Users/thomas/Temp/compressme

f1.txt (342 Bytes) -> f1.txt.rle (23 Bytes) [6%]
f2.txt (1368 Bytes) -> f2.txt.rle (83 Bytes) [6%]
f3.txt (13680 Bytes) -> f3.txt.rle (803 Bytes) [5%]

DirectoryDecompressor

Implementieren Sie eine Java-Klasse namens DirectoryDecompressor, die die Umkehroperation zum DirectoryCompressor zur Verfügung stellt. Man gibt ihr über die Kommandozeile einen Verzeichnisnamen und sie dekomprimiert alle darin gefundenen Dateien, die die Endung .rle haben.

Die komprimierten Dateien werden nach einer erfolgreichen Dekompression gelöscht.

Beachten Sie, dass auch hier unterschiedliche Fehler auftreten könne, die Sie bitte entsprechend abfangen und behandeln sollten.

Das Programm soll den Benutzer über seine Fortschritte informieren. Hierzu soll die Ausgabe ungefähr wie folgt aussehen:

Dekomprimiere Dateien in /Users/thomas/Temp/compressme2

f1.txt.rle (23 Bytes) -> f1.txt (342 Bytes)
f2.txt.rle (83 Bytes) -> f2.txt (1368 Bytes)
f3.txt.rle (803 Bytes) -> f3.txt (13680 Bytes)

Geheime Datei

Wenn Sie alles fertig implementiert haben, wenden Sie Ihre Software auf die Datei bild.bmp.rle an und dekomprimieren Sie sie.

  • Was ist zu sehen?
  • Um welchen Faktor wurde die Datei komprimiert?
  • Warum ist der Kompressionsfaktor hier so hoch?