CodeGym /Kurse /JAVA 25 SELF /Globbing/PathMatcher, DirectoryStream.Filter

Globbing/PathMatcher, DirectoryStream.Filter

JAVA 25 SELF
Level 39, Lektion 2
Verfügbar

1. Einführung

Wenn Sie mit Dateien und Ordnern arbeiten, müssen Sie oft nur bestimmte Dateien auswählen: zum Beispiel alle ".java"-Dateien, alle Bilder, alle Logs für ein bestimmtes Datum. Dafür verwendet man in Java (und nicht nur dort) Globbing (glob) und reguläre Ausdrücke (regex).

  • Globbing — eine einfache Art, Dateinamenmuster mit speziellen Zeichen zu beschreiben (*, ?, [], {}), wie in der Kommandozeile von Linux oder Windows.
  • Regex — eine mächtige Sprache für reguläre Ausdrücke, geeignet für komplexe Muster.

Beispiele für Globbing-Muster

  • *.java — alle Dateien mit der Erweiterung ".java" im aktuellen Ordner.
  • **/*.java — alle ".java"-Dateien in allen Unterordnern (doppelter Stern — rekursive Suche).
  • *.{png,jpg} — alle Dateien mit der Erweiterung ".png" oder ".jpg".
  • file-??.log — Dateien wie "file-01.log", "file-AB.log" (zwei beliebige Zeichen).
  • [A-Z]*.txt — alle ".txt"-Dateien, die mit einem Großbuchstaben beginnen.

Globbing ist einfacher als regex und reicht für die Dateifilterung meist aus.

Vergleich von glob und regex

Eigenschaft glob regex
Einfachheit Sehr einfach Komplexer, aber mächtiger
Zeichen
*, ?, [], {}
Alle Möglichkeiten von regex
Beispiele
*.java
.*\.java
Rekursion
**/*.java
Keine eingebaute Unterstützung
Wann verwenden Dateifilterung Komplexe Prüfungen

2. PathMatcher: Dateifilterung per Muster

In Java NIO (java.nio.file) verwendet man für die Filterung per Muster das Interface PathMatcher. Man erhält es über FileSystems.getDefault().getPathMatcher(...).

Syntax

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
  • "glob:*.java" — Globbing-Muster.
  • "regex:.*\\.java" — regulärer Ausdruck.

Beispiel: Dateien in einem Verzeichnis filtern

import java.nio.file.*;

Path dir = Paths.get("src");
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");

try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
    for (Path entry : stream) {
        if (matcher.matches(entry.getFileName())) {
            System.out.println(entry);
        }
    }
}

Wichtig:

  • matcher.matches() prüft in der Regel nur den Dateinamen — übergeben Sie entry.getFileName() und nicht den vollständigen Pfad.
  • Für die rekursive Suche verwenden Sie Files.walk() oder Files.find().

Files.walk() — eine NIO2-Methode, die rekursiv einen Stream<Path> mit allen Dateien und Ordnern im angegebenen Verzeichnis inklusive Unterordnern zurückgibt. Im Gegensatz zu DirectoryStream, das nur den Inhalt eines einzelnen Ordners zeigt, erlaubt Files.walk() die Arbeit mit dem Verzeichnisbaum über die Stream-API.

Beispiel:

import java.nio.file.*;
import java.util.stream.Stream;

Path start = Paths.get("src");
try (Stream<Path> stream = Files.walk(start)) { // rekursiv alle Unterordner
    stream.filter(Files::isRegularFile)
          .forEach(System.out::println);
}

Beispiel mit regex

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("regex:.*\\.(png|jpg)");

3. Files.newDirectoryStream: Filtern beim Auflisten eines Verzeichnisses

Die Methode Files.newDirectoryStream() ermöglicht es, Dateien direkt per Muster oder mit einem eigenen Filter zu filtern.

Filtern per Glob-Muster

try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
    for (Path entry : stream) {
        System.out.println(entry);
    }
}

Der zweite Parameter ist ein Glob-Muster (ohne Präfix "glob:").

Filtern mit DirectoryStream.Filter

Wenn Sie komplexere Logik brauchen (z. B. Filtern nach Größe, Datum, Ausschluss von Ordnern), verwenden Sie DirectoryStream.Filter<Path>:

DirectoryStream.Filter<Path> filter = path -> {
    // Beispiel: nur Dateien, keine Ordner, und weder .git noch node_modules
    return Files.isRegularFile(path)
        && !path.getFileName().toString().equals(".git")
        && !path.getFileName().toString().equals("node_modules");
};

try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
    for (Path entry : stream) {
        System.out.println(entry);
    }
}

4. Files.find: leistungsstarke Suche mit BiPredicate

Wenn Sie Dateien nach komplexen Bedingungen suchen müssen (z. B. nach Datum, Größe, Name, rekursiv), verwenden Sie Files.find.

Syntax

Stream<Path> stream = Files.find(
    startDir, // wo gesucht wird
    maxDepth, // Tiefe (Integer.MAX_VALUE — rekursiv)
    (path, attrs) -> {
        // path — Pfad zur Datei
        // attrs — Dateiattribute (Größe, Datum usw.)
        return path.getFileName().toString().endsWith(".log")
            && attrs.size() > 1024; // nur große Logdateien
    }
);

stream.forEach(System.out::println);
  • Der zweite Parameter ist die maximale Suchtiefe.
  • Der dritte — BiPredicate<Path, BasicFileAttributes>: gibt true zurück, wenn die Datei passt.

Beispiel: Ordner .git und node_modules ausschließen

Stream<Path> stream = Files.find(
    Paths.get("."),
    Integer.MAX_VALUE,
    (path, attrs) -> {
        String name = path.getFileName().toString();
        // Ordner .git und node_modules ausschließen
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Nur .log-Dateien größer als 1 MB
        return name.endsWith(".log") && attrs.size() > 1024 * 1024;
    }
);
stream.forEach(System.out::println);

5. Praxis

Beispiel 1: Alle .log-Dateien finden, außer .git und node_modules

Files.walk(Paths.get("."))
    .filter(path -> {
        String name = path.getFileName().toString();
        // Ordner ausschließen
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Nur .log-Dateien
        return name.endsWith(".log");
    })
    .forEach(System.out::println);

Beispiel 2: Alle Bilder finden, die nach einem bestimmten Datum erstellt wurden

import java.nio.file.attribute.BasicFileAttributes;
import java.time.Instant;

Instant after = Instant.parse("2024-06-01T00:00:00Z");

Files.find(
    Paths.get("images"),
    Integer.MAX_VALUE,
    (path, attrs) -> {
        String name = path.getFileName().toString();
        return (name.endsWith(".png") || name.endsWith(".jpg"))
            && attrs.creationTime().toInstant().isAfter(after);
    }
).forEach(System.out::println);

Beispiel 3: Alle großen Dateien (größer als 10 MB) finden, außer .git

Files.find(
    Paths.get("."),
    Integer.MAX_VALUE,
    (path, attrs) -> {
        String name = path.getFileName().toString();
        return !name.equals(".git") && attrs.size() > 10 * 1024 * 1024;
    }
).forEach(System.out::println);

6. Nützliche Details

Kurze Übersicht zur Glob-Syntax

  • * — beliebig viele beliebige Zeichen (außer dem Trenner /)
  • ** — beliebig viele beliebige Ordner (funktioniert in Java)
  • ? — genau ein beliebiges Zeichen
  • [abc] — eines der Zeichen a, b, c
  • [a-z] — ein Zeichen aus dem Bereich
  • {a,b,c} — einer der aufgeführten Werte (zum Beispiel *.{png,jpg})

Beispiele:

  • *.java — alle ".java"-Dateien im aktuellen Ordner
  • **/*.java — alle ".java"-Dateien in allen Unterordnern
  • file-??.log — Dateien wie "file-01.log", "file-AB.log"
  • [A-Z]*.txt — alle ".txt"-Dateien, die mit einem Großbuchstaben beginnen

Performance und Lecks: Schließen Sie DirectoryStream!

Wichtig!

  • DirectoryStream und Streams aus Files.find/Files.walk sind Ressourcen, die geschlossen werden müssen.
  • Verwenden Sie try-with-resources:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
    for (Path entry : stream) {
        // ...
    }
}
  • Wenn Sie den Stream nicht schließen, kann es zu Ressourcenlecks kommen (z. B. „Zu viele geöffnete Dateien“).
  • Bei Files.find und Files.walk gilt: unbedingt close() aufrufen oder try-with-resources verwenden:
try (Stream<Path> stream = Files.find(...)) {
    stream.forEach(System.out::println);
}

7. Fazit und typische Fehler

Fehler Nr. 1: Ein Glob-Muster verwenden, ohne zu beachten, dass * nicht rekursiv sucht. Für Rekursion verwenden Sie "**/*.java" oder Files.walk.

Fehler Nr. 2: Den vollständigen Pfad an matcher.matches() übergeben — in der Regel sollte nur der Dateiname (getFileName()) übergeben werden.

Fehler Nr. 3: DirectoryStream oder Stream<Path> nicht geschlossen — führt zu Ressourcenlecks.

Fehler Nr. 4: Zu komplexe regex-Muster für einfache Filter — verwenden Sie glob, wenn es ausreicht.

Fehler Nr. 5: Systemordner (".git", "node_modules") nicht ausgeschlossen — die Suche wird langsam und mit unnötigen Dateien „zugemüllt“.

1
Aufgabe
JAVA 25 SELF, Level 39, Lektion 2
Gesperrt
Datenanalyst: Auswahl kritischer Berichte
Datenanalyst: Auswahl kritischer Berichte
1
Aufgabe
JAVA 25 SELF, Level 39, Lektion 2
Gesperrt
Galeriekurator: Inventarisierung von Bildern
Galeriekurator: Inventarisierung von Bildern
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION