Einführung
Ein Archiv kann identische Bilder unter verschiedenen Namen und unterschiedliche Bilder gleicher Größe enthalten. Du implementierst den Teil eines wiederverwendbaren Dublettenfinders, der Bytes liest, und speicherst ein vollständiges Inventar, ohne die Originale zu verändern.
Du solltest Java-Klassen, Methoden, Listen, Ausnahmen und einfache Dateizugriffe kennen. Dieses Lab führt binäre Eingaben, das automatische Schließen von Ressourcen und SHA-256 ein. Verzeichnisadapter und Starter sind vorbereitet; du implementierst den Hashwert. Dies ist eine reine Leseübung, kein Werkzeug zur Datenträgerbereinigung. Die Autorenschätzung beträgt 50 Minuten einschließlich Lesen und üblicher Korrekturen.
Bytes statt Dateinamen lesen
In diesem Schritt vergleichst du zunächst ein normales Bildbeispiel und implementierst dann einen Hash, der jedes Byte genau einmal liest. Öffne WebIDE, wähle Terminal > New Terminal und verwende durchgehend dieses integrierte Terminal. Jedes Lab beginnt unabhängig mit eigenen Dateien.
cd ändert das Arbeitsverzeichnis dieses Terminals. Das vorbereitete run.sh kompiliert src/*.java in einem neuen temporären Build-Verzeichnis und führt die angegebene Java-Klasse aus. Der Compiler verwendet -encoding UTF-8, damit der Quelltext nicht von der Spracheinstellung des Terminals abhängt. Nur Compiler und Java-Unterprozesse verwenden C.UTF-8; der Java-Unterprozess nutzt außerdem UTF-8 als Standard für Text. Damit funktionieren die unterstützten UTF-8-Dateinamen, ohne deine Shell-Einstellungen zu ändern.
cd /home/labex/project/duplicates
./run.sh Sample
Das Hilfsprogramm dekodiert die echten PNG-Dateien und vergleicht ihre Bytes. Es gibt red = renamed: true und red = blue: false aus. Alle drei Bilder haben dieselben Abmessungen und dieselbe Bytelänge. Name oder Länge können eine Suche eingrenzen, beweisen aber keine Gleichheit. Optional kannst du in WebIDE Explorer duplicates/data/gallery aufklappen und red.png, blue.png sowie copies/renamed.png zum Anzeigen anklicken. Verbindlich sind die Dekodierung und das Byteergebnis von Sample oben. Es handelt sich um eigene synthetische Kursbilder; externe Materialien werden nicht benötigt.
Die öffentliche Schnittstelle Digester vereinbart eine Methode: digest(Path) liefert eine hexadezimale Zeichenfolge oder wirft bei einem Lesefehler IOException. Path bezeichnet eine Datei; ein InputStream liest binäre Bytes, ohne sie als Text zu interpretieren. SHA-256 verdichtet diese Bytes zu einem Kandidatenfingerabdruck fester Länge. Eine spätere Einheit vergleicht trotzdem den tatsächlichen Inhalt, bevor sie Dateien als Duplikate bezeichnet.
Lies den kleinen Adapter und die Ausgangsimplementierung, bevor du diese ersetzt:
cat src/Digester.java src/Sha256.java
Ein Puffer mit 1024 Bytes wird wiederverwendet. read(buffer) liefert die Anzahl der diesmal gültigen Bytes oder am Ende -1. Der letzte Lesezugriff füllt eventuell nur einen Teil des Puffers; die übrigen Bytes behalten frühere Werte. Deshalb muss sha.update(buffer, 0, count) die tatsächlich gelesene Anzahl verwenden. Eine leere Datei führt keine Aktualisierungen aus und besitzt dennoch einen gültigen Hashwert.
try (InputStream ... ) ist try-with-resources: Java schließt den Stream beim Verlassen des Blocks, auch wenn das Lesen eine Ausnahme auslöst. Das Schließen gibt die Dateiressource frei; es verspricht keine sofortige Garbage Collection. MessageDigest.getInstance kann eine geprüfte Ausnahme zum Algorithmus auslösen. Der äußere catch wandelt sie deshalb in denselben Fehlervertrag für Dateioperationen um. Hexadezimal benötigt zwei Zeichen pro Byte; & 255 behandelt ein vorzeichenbehaftetes Java-Byte als vorzeichenlosen Wert.
Der nächste Befehl schreibt den vollständigen Ersatz. > ersetzt diese Quelldatei. Der Here-Document-Marker in Anführungszeichen <<'JAVA' übergibt den dazwischenliegenden Text unverändert an cat; das abschließende JAVA muss allein und ohne Einrückung in seiner Zeile stehen. Du kannst dieselbe Datei stattdessen in WebIDE bearbeiten und speichern. Bleibt das Terminal nach dem Einfügen bei einer unvollständigen Here-Document-Eingabe stehen, brich mit Ctrl+C ab und füge den gesamten Ersatzbefehl einschließlich Endmarker erneut ein.
cat > src/Sha256.java <<'JAVA'
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Sha256 implements Digester {
public String digest(Path path) throws IOException {
try {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] buffer = new byte[1024];
try (InputStream input = Files.newInputStream(path)) {
int count;
while ((count = input.read(buffer)) != -1) {
sha.update(buffer, 0, count);
}
}
StringBuilder hex = new StringBuilder();
for (byte value : sha.digest()) {
hex.append(String.format("%02x", value & 255));
}
return hex.toString();
} catch (NoSuchAlgorithmException error) {
throw new IOException("SHA-256 unavailable", error);
}
}
}
JAVA
Kompiliere das gleiche Beispiel erneut und führe es aus. Dabei wird die neue Klasse geprüft; die Gleichheitsentscheidungen des Beispiels bleiben unverändert.
./run.sh Sample
Nennt Java eine Zeilennummer, öffne src/Sha256.java erneut, vergleiche Klammern und Schreibweisen mit dem Ersatz, speichere und starte neu. Ist ein Pfad falsch, zeigt pwd dein aktuelles Verzeichnis; wiederhole anschließend das absolute cd oben. Du musst die VM nicht zurücksetzen.
Jeden Pfad mit seinem Fingerabdruck speichern
In diesem Schritt erstellst du mit deinem Hash ein vollständiges UTF-8-Inventar. Arbeite im selben integrierten WebIDE-Terminal unter /home/labex/project/duplicates weiter.
Der vorbereitete Adapter Inputs.files durchläuft nur das angegebene Verzeichnis und schließt dessen Verzeichnisstream. Er lehnt symbolische Links, Spezialdateien und Pfade mit Tabulatoren oder Zeilenumbrüchen ab, statt ihnen stillschweigend zu folgen oder sie zu überspringen. Unterstützt werden ausschließlich stabile gewöhnliche Dateien und Verzeichnisse. Verändere Eingaben nicht während des Scans. Attributprüfungen erkennen einige Änderungen, frieren das Verzeichnis jedoch nicht ein und garantieren keinen nebenläufigen Snapshot.
Das vorbereitete Inventory speichert nach erfolgreichem Abschluss aller Lesezugriffe jeden relativen Pfad, seine Größe und seinen Hash. Das TSV-Format trennt Spalten durch Tabulatoren. Der Berichtsadapter schreibt UTF-8 in eine neue temporäre Datei und ersetzt anschließend den Berichtspfad. Daher kann auch ein bestehender Bericht als Hardlink keine Originalbytes überschreiben. Dies garantiert keine Dauerhaftigkeit bei einem Absturz. Die Ausgabe muss außerhalb der gescannten Wurzel liegen. reports existiert bereits neben data, damit der Bericht nicht versehentlich zur eigenen Eingabe wird. Die Reihenfolge lautet Inventory ROOT REPORT: zuerst das zu scannende Verzeichnis, dann der Ausgabepfad; vertausche die Argumente nicht.
./run.sh Inventory data/gallery reports/inventory.tsv
Erwarte Inventory complete: 11 files. Mit cat liest du den gespeicherten Text:
cat reports/inventory.tsv
Die Kopfzeile lautet PATH, SIZE, SHA256. Vergleiche die Zeilen für red.png, copies/renamed.png und copies/third.png: verschiedene Pfade teilen denselben Hash. blue.png hat dieselbe Größe, aber einen anderen Hash. Beide leeren Dateien haben Größe null und denselben gültigen Hash. tail.bin endet mit einem teilweise gefüllten Puffer; seine identische Kopie muss trotzdem denselben Hash erhalten. Die gleichnamigen north/readme.txt und south/readme.txt behalten getrennte Zeilen.
Übe jetzt einen gewöhnlichen Pfadfehler. Der folgende Befehl scheitert absichtlich mit Exitstatus 2 und Scan failed; er darf keinen neuen Bericht erzeugen. Ein Fehler ist kein erfolgreiches leeres Inventar. Ein älterer Bericht belegt nicht den Erfolg dieses fehlgeschlagenen Laufs.
./run.sh Inventory data/missing reports/missing.tsv
Korrigiere den Eingabepfad und erstelle einen neuen Bericht:
./run.sh Inventory data/gallery reports/recovered.tsv
Für Lesefehler gilt dieselbe Regel: Zeige den Fehler und melde einen Fehlschlag, statt eine unlesbare Datei stillschweigend auszulassen. Die Prüfungen führen deinen aktuellen Quelltext auf isolierten Testdateien für leere Inhalte, unvollständige letzte Puffer und Lesefehler aus und lesen das echte Inventar. Alle Originale müssen Pfade, Bytes, Zugriffsrechte, Eigentümer und Änderungszeiten behalten; lösche oder ersetze sie nicht. Gewöhnliche Lesezugriffe können Zugriffszeiten ändern. Du hast jetzt Kandidatenfakten; im nächsten Lab werden gleiche Fingerabdrücke Kandidaten für einen echten Bytevergleich.
Zusammenfassung
Du hast Binärdateien mit try-with-resources gelesen, SHA-256 mit der tatsächlich gelesenen Anzahl aktualisiert und ein vollständiges UTF-8-Inventar gespeichert. Namen, Größen und Hashwerte sind Kandidatenfakten; echte Bytevergleiche bestätigen die Duplikatgruppen.



