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

Globbing/PathMatcher, DirectoryStream.Filter

JAVA 25 SELF
Level 39 , Lesson 2
Available

1. Introduction

When you work with files and directories, you often need to select only certain files: for example, all ".java" files, all images, or all logs for a specific date. To do this in Java (and elsewhere), people use globbing (glob) and regular expressions (regex).

  • Globbing — a simple way to describe a file name pattern using special characters (*, ?, [], {}), like in a Linux or Windows command line.
  • Regex — a powerful regular-expression language for complex patterns.

Examples of globbing patterns

  • *.java — all files with the ".java" extension in the current directory.
  • **/*.java — all ".java" files in all subdirectories (double asterisk — recursive search).
  • *.{png,jpg} — all files with the ".png" or ".jpg" extension.
  • file-??.log — files like "file-01.log", "file-AB.log" (any two characters).
  • [A-Z]*.txt — all ".txt" files that start with an uppercase letter.

Globbing is simpler than regex, and most of the time it’s enough for file filtering.

Glob vs. regex

Feature glob regex
Simplicity Very simple More complex but more powerful
Symbols
*, ?, [], {}
Full regex capabilities
Examples
*.java
.*\.java
Recursion
**/*.java
No built-in support
When to use File filtering Complex checks

2. PathMatcher: filtering files by pattern

In Java NIO (java.nio.file), to filter files by a pattern use the PathMatcher interface. You can obtain it via FileSystems.getDefault().getPathMatcher(...).

Syntax

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
  • "glob:*.java" — a globbing pattern.
  • "regex:.*\\.java" — a regular expression pattern.

Example: filtering files in a directory

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

Important:

  • matcher.matches() usually checks only the file name — pass entry.getFileName(), not the full path.
  • For recursive search, use Files.walk() or Files.find().

Files.walk() is an NIO2 method that returns a Stream<Path> with all files and directories in the specified directory recursively, including subdirectories. Unlike DirectoryStream, which shows only the contents of a single directory, Files.walk() lets you work with the directory tree via the Stream API.

Usage example:

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

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

Example with regex

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

3. Files.newDirectoryStream: filtering while listing a directory

The Files.newDirectoryStream() method lets you filter files by pattern or with a custom filter right away.

Filtering by a glob pattern

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

The second parameter is a glob pattern (without the "glob:" prefix).

Filtering with DirectoryStream.Filter

If you need more complex logic (for example, filtering by size, date, or excluding directories), use DirectoryStream.Filter<Path>:

DirectoryStream.Filter<Path> filter = path -> {
    // Example: files only (no directories), and not .git or 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: powerful search with BiPredicate

If you need to search for files by complex conditions (for example, by date, size, name, recursively), use Files.find.

Syntax

Stream<Path> stream = Files.find(
    startDir, // where to search
    maxDepth, // depth (Integer.MAX_VALUE — recursive)
    (path, attrs) -> {
        // path — path to the file
        // attrs — file attributes (size, date, etc.)
        return path.getFileName().toString().endsWith(".log")
            && attrs.size() > 1024; // only large logs
    }
);

stream.forEach(System.out::println);
  • The second parameter is the maximum search depth.
  • The third is BiPredicate<Path, BasicFileAttributes>: returns true if the file matches.

Example: exclude .git and node_modules directories

Stream<Path> stream = Files.find(
    Paths.get("."),
    Integer.MAX_VALUE,
    (path, attrs) -> {
        String name = path.getFileName().toString();
        // Exclude .git and node_modules directories
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Only .log files larger than 1 MB
        return name.endsWith(".log") && attrs.size() > 1024 * 1024;
    }
);
stream.forEach(System.out::println);

5. Practice

Example 1: Find all .log files except .git and node_modules

Files.walk(Paths.get("."))
    .filter(path -> {
        String name = path.getFileName().toString();
        // Exclude directories
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Only .log files
        return name.endsWith(".log");
    })
    .forEach(System.out::println);

Example 2: Find all images created after a specific date

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);

Example 3: Find all large files (more than 10 MB) except .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. Useful nuances

Quick cheat sheet for glob syntax

  • * — any number of any characters (except the separator /)
  • ** — any number of any directories (works in Java)
  • ? — exactly one character
  • [abc] — any of the characters a, b, c
  • [a-z] — any character from the range
  • {a,b,c} — any of the listed values (for example, *.{png,jpg})

Examples:

  • *.java — all ".java" files in the current directory
  • **/*.java — all ".java" files in all subdirectories
  • file-??.log — files like "file-01.log", "file-AB.log"
  • [A-Z]*.txt — all ".txt" files that start with an uppercase letter

Performance and leaks: close DirectoryStream!

Important!

  • DirectoryStream and the streams from Files.find/Files.walk are resources that must be closed.
  • Use try-with-resources:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
    for (Path entry : stream) {
        // ...
    }
}
  • If you don’t close the stream, resource leaks may occur (for example, “too many open files”).
  • For Files.find and Files.walk, make sure to call close() or use try-with-resources:
try (Stream<Path> stream = Files.find(...)) {
    stream.forEach(System.out::println);
}

7. Summary and common mistakes

Error #1: Using a glob pattern without understanding that * is not recursive. For recursion, use "**/*.java" or Files.walk.

Error #2: Passing the full path to matcher.matches() — you usually need to pass only the file name (getFileName()).

Error #3: Forgetting to close DirectoryStream or Stream<Path> — you’ll get a resource leak.

Error #4: Overly complex regex patterns for simple filtering — use glob if it’s sufficient.

Error #5: Not excluding special directories (".git", "node_modules") — the search becomes slow and cluttered with unnecessary files.

1
Task
JAVA 25 SELF, level 39, lesson 2
Locked
Data Analyst: selecting critical reports
Data Analyst: selecting critical reports
1
Task
JAVA 25 SELF, level 39, lesson 2
Locked
Gallery Curator: image inventory
Gallery Curator: image inventory
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION