Update of exercises

main
Thomas Smits 2026-05-20 13:29:55 +02:00
parent 9e93d39d95
commit 61c32b6bbd
12 changed files with 252 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1,133 @@
# Mist die Festplatte ist schon wieder voll
**🎓 Benotetes Assignment 🎓**
📆 **Fällig: 03.06.2026** 📆
Wenn wir ehrlich sind ist es auch nur begrenzt spannend, den ganzen Tag Kaninchen dabei zuzusehen, wie diese Fibonacci-Folgen produzieren. Auch die ganze Ruhe in den Alpen geht Ihnen inzwischen schwer auf die Nerven.
Endlich ist der Urlaub zuende und Sie kommen entspannt wieder nach Hause. Sogar der RE 10b ist pünktlich in Mannheim, sodass noch etwas vom Tag übrig bliebt.
Was kann mehr entspannen als ... eine Runde Battlefield 6.
![](img/disk_full.jpg)
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](../sources/src/main/java/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 `255`, das Byte und die Anzahl der Wiederholungen geschrieben werden. So wird z.B. aus der Bytefolge `{ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 }` durch die Kompression die Folge `{ 0xff, 0x05, 0x09 }`. Wiederholt sich ein Byte nicht, wird es einfach direkt übernommen. Es sind maximal 253 Wiederholungen möglich. Kommt ein Byte häufiger als 254-mal hintereinander vor, werden zuerst die ersten 254 Byte komprimiert und dann die nächsten Bytes bearbeitet. (Die Bytewerte sind hier als Hexadezimalzahlen angegeben, `0xff` würde dann in dezimalen Darstellung `255` entsprechen.)
Beachten Sie den Spezialfall, dass die Eingabedaten eine `255` (`0xff`) enthalten, in diesem Fall müssen Sie die Escapesequenz `{ 0xff, 0xff }` schreiben, um dies bei der Dekompression erkennen zu können. So wird z.B. aus der Eingabe `{ 0x03, 0xff, 0x05 }` komprimiert `{ 0x03, 0xff, 0xff, 0x05 }`.
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 |
|---------------------------------------------------------|-----|--------------------------------------------------|
| `0x00, 0x01, 0x02, 0x03` | -> | `0x00, 0x01, 0x02, 0x03` |
| `0xff, 0xff, 0xff` | -> | `0xff, 0xff, 0xff, 0xff, 0xff, 0xff` |
| `0xff, 0x01, 0x02, 0x03` | -> | `0xff, 0xff, 0x01, 0x02, 0x03` |
| `0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01` | -> | `0x00, 0xff, 0x01, 0x06` |
| `0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05` | -> | `0xff, 0x05, 0x09` |
| `0x00, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03` | -> | `0x00, 0xff, 0x01, 0x02, 0xff, 0x03, 0x04` |
| `0x01, 0x01, 0x03, 0x03, 0x03, 0x03` | -> | `0xff, 0x01, 0x02, 0xff, 0x03, 0x04` |
| `0x01, 0x02, 0x01, 0x03, 0x03, 0x03` | -> | `0x01, 0x02, 0x01, 0xff, 0x03, 0x03` |
| `0x01, 0x01, 0xff, 0x01, 0x02, 0x03` | -> | `0xff, 0x01, 0x02, 0xff, 0xff, 0x01, 0x02, 0x03` |
| `0x01, 0x01, 0xff` | -> | `0xff, 0x01, 0x02, 0xff, 0xff` |
| `0xff` | -> | `0xff, 0xff` |
| `0xff, 0x01, 0x01` | -> | `0xff, 0xff, 0xff, 0x01, 0x02` |
| `0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01` | -> | `0xff, 0x01, 0x09` |
| `0x01, 0x01, 0x01, 0x01, 0xff` | -> | `0xff, 0x01, 0x04, 0xff, 0xff` |
## 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:
```console
$ 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:
```console
$ 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 die bereits komprimierten Dateien informieren. Hierzu soll die Ausgabe ungefähr wie folgt aussehen:
```console
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%]
```
Für jede komprimierte Datei wird eine neue Zeile ausgegeben.
## 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:
```console
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](files/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?

View File

@ -27,6 +27,7 @@ Wichtige Einstellungen für Eclipse sind [hier](help/eclipse.md) beschrieben.
| 8. | 29.04.2026 | [Ein Traum in Zucker oder kann ich dem Zufallsgenerator trauen?](Assignment_008/readme.md) | **05.05.2026** |
| 9. | 06.05.2026 | [Was hoppelt den da?](Assignment_009/readme.md) | **19.05.2026** |
| 10. | 13.05.2026 | [Live-Testat](Assignment_010/readme.md) | **13.05.2026** |
| 11. | 20.05.2026 | [Mist die Festplatte ist schon wieder voll](Assignment_011/readme.md) | **03.06.2026** |
## 🏛️ Aufbau der Veranstaltung

View File

@ -15,7 +15,7 @@ public class DrawRandom {
private static final int W = 100;
private static final int H = 100;
private static final int I = 50_000_000;
private static final int I = 5_000_000;
private static void generate(RandomGeneratorFactory factory,
String fn) throws IOException {
@ -65,7 +65,7 @@ public class DrawRandom {
int x = i % W;
int y = i / W;
int gray = (int) ((c[i] / (double) max) * 255);
int gray = (int) ((c[i] / (double) max));
int rgb = gray * 65536 + gray * 256 + gray;
image.setRGB(x, y, rgb);
@ -77,8 +77,6 @@ public class DrawRandom {
public static void main(String[] args) throws Exception {
generate(StandardRandomGenerator::new, "standard_random.png");
generate(SecRandomGenerator::new, "secure_random.png");
generate(SuperSecRandomGenerator::new, "super_secure_random.png");
System.out.println("Alle Bilder erfolgreich erzeugt.");
}
}

View File

@ -0,0 +1,14 @@
package pr2.io.compressor;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Stream implementation that performs an automatic run-length
* encoding of the written data.
*/
public class CompressingOutputStream {
// TODO: Klasse implementieren
}

View File

@ -0,0 +1,13 @@
package pr2.io.compressor;
import java.io.IOException;
import java.io.InputStream;
/**
* Stream that automatically decompresses data that was compressed
* with the {@link CompressingOutputStream}.
*/
public class DecompressingInputStream {
// TODO: Klasse implementieren
}

View File

@ -0,0 +1,20 @@
package pr2.io.compressor.tool;
import java.io.File;
import java.io.FileFilter;
/**
* Compresses the content of a directory.
*/
public class DirectoryCompressor extends DirectoryToolBase {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Bitte geben Sie ein Verzeichnis an.");
System.exit(1);
}
// TODO: Methode implementieren
}
}

View File

@ -0,0 +1,20 @@
package pr2.io.compressor.tool;
import java.io.File;
import java.io.FileFilter;
/**
* Decompresses the content of a directory.
*/
public class DirectoryDecompressor extends DirectoryToolBase {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Bitte geben Sie ein Verzeichnis an.");
System.exit(1);
}
// TODO: Methode implementieren
}
}

View File

@ -0,0 +1,11 @@
package pr2.io.compressor.tool;
import java.io.File;
/**
* Base class for all directory based tools.
*/
public class DirectoryToolBase {
// TODO: Gemeinsame Methoden für DirectoryCompressor und DirectoryDecompressor
}

View File

@ -0,0 +1,19 @@
package pr2.io.compressor.tool;
import pr2.io.compressor.CompressingOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Compresses a single file or stdin.
*/
public class FileCompressor {
public static void main(String... args) throws Exception {
// TODO: Methode implementieren
}
}

View File

@ -0,0 +1,19 @@
package pr2.io.compressor.tool;
import pr2.io.compressor.DecompressingInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Decompresses a single file or stdin.
*/
public class FileDecompressor {
public static void main(String... args) throws Exception {
// TODO: Methode implementieren
}
}