Update of exercises

main
Thomas Smits 2025-11-19 12:35:01 +01:00
parent 55ab331dac
commit 44614904a9
13 changed files with 259 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

@ -0,0 +1,131 @@
# 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.
<img width="400" src="img/disk_full.jpg" alt="disk full">
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 `-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:
```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 seine Fortschritte 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%]
```
## 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

@ -173,6 +173,7 @@ Wichtige Einstellungen für Eclipse sind [hier](help/eclipse.md) beschrieben.
| 6. | 22.10.2025 | [Racewars](Assignment_006/readme.md) | **04.11.2025** |
| 7. | 05.11.2025 | [Ein Traum in Zucker oder kann ich dem Zufallsgenerator trauen?](Assignment_007/readme.md) | **18.11.2025** |
| 8. | 05.11.2025 | [Live-Testat](Assignment_008/readme.md) | **19.11.2025** |
| 9. | 19.11.2025 | [Mist die Festplatte ist schon wieder voll](Assignment_009/readme.md) | **02.12.2025** |
## 😀 Freiwillige Übungen
Die freiwilligen Übungen zur Vertiefung der Vorlesungsinhalte finden Sie in einem getrennten Repository: [Freiwillige Übungen](/pr2-lecture/uebungen/src/branch/master/readme.md).

View File

@ -6,7 +6,7 @@ package pr2.algorithmen.random;
public interface RandomGenerator {
/**
* Generate a new random number between 0 and max (excluded).
* Generate a new random number between 0 and max (excluded).
*
* @param max the maximum value (excluding) to be generated
* @return a random number between 0 and max (exclusive)

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
}
}

View File

@ -2,7 +2,14 @@ package pr2.vererbung.racewars.model;
/**
* Die Klasse repräsentiert die vier Elemente.
* <ul>
* <li>Feuer ist Luft überlegen,</li>
* <li>Wasser ist Feuer überlegen,</li>
* <li>Erde ist Wasser überlegen,</li>
* <li>Luft ist Erde überlegen.</li>
* </ul>
*/
public final class Element {
}

View File

@ -10,7 +10,9 @@ public final class WesenFactory {
* Der Methode übergibt man die Information, wie viel Geld man investieren
* möchte, und sie gibt ein Array von Wesen zurück. Das erste Element ist
* immer der Anführer, die folgenden dann entsprechend die Wesen der Art.
* Reicht das Geld nicht, wird ein leeres Array zurückgegeben.
* Reicht das Geld nicht, wird ein leeres Array zurückgegeben. Reicht
* das Geld nicht für den Anführer, sondern nur für normale Wesen,
* wird der Anführer übersprungen.
* Die Art der erzeugten Wesen wählt man über einen passenden Parameter aus.
*
* @param rasse Rasse, von der man Wesen erzeugen möchte