HOWTO · Java

Vergleichen Sie den Inhalt zweier Dateien in Java

Dieses Tutorial zeigt, wie Sie den Inhalt zweier Dateien in Java vergleichen.

Auf dieser Seite

In Java gibt es mehrere Möglichkeiten, Dateien zu vergleichen – je nachdem, ob Sie exakt gleiche Bytes, gleiche Textzeilen oder die Position des ersten abweichenden Bytes benötigen. Verwenden Sie für große Dateien einen Streaming-Vergleich, für Text einen Zeilenvergleich der dekodierten Zeilenwerte (Zeilenabschlusszeichen werden dabei ignoriert), Files.mismatch(), sofern Ihre Laufzeitumgebung diese Methode bereitstellt, Apache Commons IO, wenn Ihr Projekt diese Abhängigkeit bereits verwendet, oder Arrays.equals(), wenn beide Dateien problemlos in den Arbeitsspeicher passen.

Eine Methode zum Dateivergleich auswählen

Methode Vergleicht Geeignet, wenn
Byte für Byte Jedes Byte, einschließlich Kodierungs- und Zeilenumbruch-Bytes Exakte Inhaltsgleichheit erforderlich ist und die Dateien groß sein können
Zeile für Zeile Dekodierte Zeilenwerte ohne Zeilenabschlusszeichen Text anhand der Zeilen statt anhand der gespeicherten Bytes übereinstimmen soll
Files.mismatch() Bytes und die Position des ersten abweichenden Bytes Die Laufzeitumgebung diese API bereitstellt und die Abweichungsposition hilfreich ist
Apache Commons IO Dateiinhalt über FileUtils.contentEquals() Das Projekt Commons IO bereits einbindet
Arrays.equals() Zwei vollständige Byte-Arrays Beide Dateien klein genug sind, um sie in den Arbeitsspeicher zu laden

Bei einem Byte-Vergleich gelten unterschiedliche Kodierungen und Zeilenumbruchfolgen als unterschiedliche Inhalte. Ein Zeilenvergleich dekodiert Zeichen, und readLine() entfernt die Zeilenabschlusszeichen. Daher können Dateien mit denselben Zeilenwerten als gleich gelten, obwohl eine LF und die andere CRLF verwendet. Auch ein abschließendes Zeilenabschlusszeichen wird dabei nicht von einem fehlenden unterschieden. Wählen Sie die Methode danach aus, was „gleich“ für Ihre Anwendung bedeutet.

Dateien in Java Byte für Byte vergleichen

Lesen Sie die Bytes beider Streams in derselben Schleife. InputStream.read() liefert einen Wert zwischen 0 und 255 oder am Dateiende -1. Wenn Sie beide Rückgabewerte prüfen, erkennen Sie sowohl unterschiedliche Dateilängen als auch abweichende Bytes. Dieser Streaming-Ansatz benötigt nur wenig, festen Speicher für den Zustand der Streams, statt beide Dateien in Arrays zu laden.

Führen Sie dieses Beispiel aus. Die Code- und Ausgabebezeichnungen bleiben auf Englisch, da der Beispielcode und seine Ausgabe unverändert übernommen werden.

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;

public class Main {
    static boolean equalByByte(Path first, Path second) throws IOException {
        try (InputStream a = Files.newInputStream(first);
             InputStream b = Files.newInputStream(second)) {
            while (true) {
                int x = a.read();
                int y = b.read();
                if (x != y) return false;
                if (x == -1) return true;
            }
        }
    }

    static boolean equalByLine(Path first, Path second) throws IOException {
        try (java.io.BufferedReader a = Files.newBufferedReader(first, StandardCharsets.UTF_8);
             java.io.BufferedReader b = Files.newBufferedReader(second, StandardCharsets.UTF_8)) {
            while (true) {
                String x = a.readLine();
                String y = b.readLine();
                if (!java.util.Objects.equals(x, y)) return false;
                if (x == null) return true;
            }
        }
    }

    static boolean equalByArrays(Path first, Path second) throws IOException {
        return Arrays.equals(Files.readAllBytes(first), Files.readAllBytes(second));
    }

    public static void main(String[] args) throws IOException {
        Path first = Files.createTempFile("first", ".txt");
        Path second = Files.createTempFile("second", ".txt");
        try {
            Files.write(first, "DelftStack\n".getBytes(StandardCharsets.UTF_8));
            Files.write(second, "DelftStack\n".getBytes(StandardCharsets.UTF_8));
            System.out.println("Byte comparison: " + equalByByte(first, second));
            System.out.println("Line comparison: " + equalByLine(first, second));
            System.out.println("Array comparison: " + equalByArrays(first, second));
        } finally {
            Files.deleteIfExists(first);
            Files.deleteIfExists(second);
        }
    }
}
Byte comparison: true
Line comparison: true
Array comparison: true

Der Stream-Vergleich liefert sofort false, wenn ein Byte abweicht oder ein Stream früher endet, und true, wenn beide gleichzeitig das Dateiende erreichen. Try-with-resources schließt beide Streams auch dann, wenn beim Lesen ein Fehler auftritt. Files.newInputStream() und andere Dateioperationen können IOException auslösen; in diesem Beispiel darf main die Ausnahme weitergeben.

Dateien in Java Zeile für Zeile vergleichen

Öffnen Sie für Text beide Dateien mit demselben ausdrücklich angegebenen Zeichensatz. Vergleichen Sie anschließend die Ergebnisse jedes readLine()-Aufrufs auf Wertgleichheit. Objects.equals() verarbeitet sowohl Zeichenfolgen als auch null: Hat nur eine Datei noch eine weitere Zeile, unterscheiden sich die Werte; liefern beide Aufrufe null, sind beide Dateien am Ende. Vergleichen Sie Zeichenfolgen nicht mit ==: Damit prüfen Sie, ob zwei Referenzen auf dasselbe Objekt verweisen, nicht ob deren Inhalte übereinstimmen.

Die zeilenweise Methode im ausführbaren Beispiel verwendet UTF-8. Wählen Sie den Zeichensatz passend zu den Dateien, die Sie einlesen möchten. Ein anderer Zeichensatz kann die dekodierten Werte verändern oder zu Dekodierungsfehlern führen. Da readLine() die Zeilenabschlusszeichen verwirft, sollten Sie stattdessen Bytes vergleichen, wenn es auf die genauen Zeilenumbruch-Bytes ankommt.

Dateien in Java mit Files.mismatch() vergleichen

Zum Beispiel liefert long position = Files.mismatch(first, second); den Wert -1, wenn die Dateien bytegenau übereinstimmen. Andernfalls gibt die Methode die nullbasierte Position des ersten abweichenden Bytes zurück. Ist eine Datei ein Präfix der anderen, entspricht diese Position dem ersten Byte nach dem Ende der kürzeren Datei. Dieses Verhalten wird in der API-Dokumentation zu Files.mismatch() beschrieben. Ob die Methode verfügbar ist, hängt von der verwendeten Java-Laufzeitumgebung ab. Prüfen Sie daher vor der Auswahl, ob die Zielumgebung sie bereitstellt.

Dateien in Java mit Apache Commons IO vergleichen

Apache Commons IO stellt FileUtils.contentEquals(File, File) bereit. Die Methode liefert einen booleschen Wert zur Inhaltsgleichheit. Binden Sie Commons IO mit der Version und der Abhängigkeitsverwaltung ein, die in Ihrem Projekt bereits vorgesehen sind; die Bibliothek gehört nicht zur Java-Standardbibliothek. Nach dem Import von org.apache.commons.io.FileUtils lautet der Aufruf beispielsweise FileUtils.contentEquals(first.toFile(), second.toFile()).

Die Methode kann IOException auslösen. Behandeln Sie diese Ausnahme oder geben Sie sie weiter, statt sie als Ergebnis des Gleichheitsvergleichs zu interpretieren. Der boolesche Rückgabewert allein beweist nicht, dass zwei vorhandene Dateien verglichen wurden: Die Methode kann true liefern, wenn beide Pfade nicht vorhanden sind. Wenn Gleichheit voraussetzt, dass tatsächlich zwei Dateien verglichen wurden, prüfen Sie vor dem Aufruf, ob beide Eingaben existieren, und behandeln Sie anschließend mögliche E/A-Fehler entsprechend den Anforderungen Ihrer Anwendung.

Dateien in Java mit Arrays.equals() vergleichen

Files.readAllBytes() lädt jede Datei vollständig in den Arbeitsspeicher; Arrays.equals(byte[], byte[]) vergleicht anschließend die erhaltenen Bytes. Die Hilfsmethode im Beispiel zeigt diesen Ansatz aus der Standardbibliothek. Er ist knapp und eignet sich, wenn die Dateien bekanntermaßen überschaubar groß sind. Bei großen Eingaben kann er jedoch den verfügbaren Arbeitsspeicher aufbrauchen. Wenn die Dateigröße ungewiss ist, verwenden Sie stattdessen den Streaming-Ansatz oder Files.mismatch(), sofern Ihre Laufzeitumgebung die Methode unterstützt.

Wählen Sie einen Zeilenvergleich, wenn der dekodierte Text maßgeblich ist, und einen Byte-Vergleich, wenn der exakt gespeicherte Inhalt zählen soll. Für große Dateien eignet sich Streaming. Verwenden Sie Files.mismatch(), wenn die Laufzeitumgebung die Methode bereitstellt und die Position der Abweichung hilfreich ist, und Apache Commons IO, wenn die Abhängigkeit bereits zum Projekt gehört. Arrays.equals() ist praktisch, wenn beide Dateien sicher vollständig in den Arbeitsspeicher passen.