JavaRush /Курсы /JAVA 25 SELF /Глоббинг/PathMatcher, DirectoryStream.Filter

Глоббинг/PathMatcher, DirectoryStream.Filter

JAVA 25 SELF
39 уровень , 2 лекция
Открыта

1. Введение

Когда вы работаете с файлами и папками, часто нужно выбрать только определённые файлы: например, все ".java"-файлы, все картинки, все логи за определённую дату. Для этого в Java (и не только) используют глоббинг (glob) и регулярные выражения (regex).

  • Глоббинг — простой способ описать шаблон имени файла с помощью специальных символов (*, ?, [], {}), как в командной строке Linux или Windows.
  • Regex — мощный язык регулярных выражений для сложных шаблонов.

Примеры глоббинг-шаблонов

  • *.java — все файлы с расширением ".java" в текущей папке.
  • **/*.java — все ".java"-файлы во всех подпапках (двойная звёздочка — рекурсивный поиск).
  • *.{png,jpg} — все файлы с расширением ".png" или ".jpg".
  • file-??.log — файлы вроде "file-01.log", "file-AB.log" (два любых символа).
  • [A-Z]*.txt — все ".txt"-файлы, начинающиеся с заглавной буквы.

Глоббинг проще, чем regex, и чаще всего его хватает для фильтрации файлов.

Сравнение glob и regex

Особенность glob regex
Простота Очень просто Сложнее, но мощнее
Символы
*, ?, [], {}
Все возможности regex
Примеры
*.java
.*\.java
Рекурсивность
**/*.java
Нет встроенной поддержки
Когда использовать Фильтрация файлов Сложные проверки

2. PathMatcher: фильтрация файлов по маске

В Java NIO (java.nio.file) для фильтрации файлов по маске используют интерфейс PathMatcher. Его можно получить через FileSystems.getDefault().getPathMatcher(...).

Синтаксис

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
  • "glob:*.java" — шаблон глоббинга.
  • "regex:.*\\.java" — шаблон регулярного выражения.

Пример: фильтрация файлов в папке

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

Важно:

  • matcher.matches() проверяет обычно только имя файла — передавайте entry.getFileName(), а не полный путь.
  • Для рекурсивного поиска используйте Files.walk() или Files.find().

Files.walk() — метод NIO2, возвращающий Stream<Path> со всеми файлами и папками в указанной директории рекурсивно, включая подпапки. В отличие от DirectoryStream, который показывает только содержимое одной папки, Files.walk() позволяет работать с деревом каталогов через Stream API.

Пример использования:

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

Path start = Paths.get("src");
try (Stream<Path> stream = Files.walk(start)) { // рекурсивно все подпапки
    stream.filter(Files::isRegularFile)
          .forEach(System.out::println);
}

Пример с regex

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

3. Files.newDirectoryStream: фильтрация при просмотре папки

Метод Files.newDirectoryStream() позволяет сразу фильтровать файлы по маске или с помощью собственного фильтра.

Фильтрация по glob-шаблону

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

Второй параметр — это glob-шаблон (без префикса "glob:").

Фильтрация с помощью DirectoryStream.Filter

Если нужна более сложная логика (например, фильтрация по размеру, дате, исключение папок), используйте DirectoryStream.Filter<Path>:

DirectoryStream.Filter<Path> filter = path -> {
    // Пример: только файлы, не папки, и не .git, не 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: мощная выборка с BiPredicate

Если нужно искать файлы по сложным условиям (например, по дате, размеру, имени, рекурсивно), используйте Files.find.

Синтаксис

Stream<Path> stream = Files.find(
    startDir, // где искать
    maxDepth, // глубина (Integer.MAX_VALUE — рекурсивно)
    (path, attrs) -> {
        // path — путь к файлу
        // attrs — атрибуты файла (размер, дата и т.д.)
        return path.getFileName().toString().endsWith(".log")
            && attrs.size() > 1024; // только большие логи
    }
);

stream.forEach(System.out::println);
  • Второй параметр — максимальная глубина поиска.
  • Третий — BiPredicate<Path, BasicFileAttributes>: возвращает true, если файл подходит.

Пример: исключить папки .git и node_modules

Stream<Path> stream = Files.find(
    Paths.get("."),
    Integer.MAX_VALUE,
    (path, attrs) -> {
        String name = path.getFileName().toString();
        // Исключаем папки .git и node_modules
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Только .log-файлы размером больше 1 МБ
        return name.endsWith(".log") && attrs.size() > 1024 * 1024;
    }
);
stream.forEach(System.out::println);

5. Практика

Пример 1: Найти все .log-файлы, кроме .git и node_modules

Files.walk(Paths.get("."))
    .filter(path -> {
        String name = path.getFileName().toString();
        // Исключаем папки
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Только файлы .log
        return name.endsWith(".log");
    })
    .forEach(System.out::println);

Пример 2: Найти все картинки, созданные после определённой даты

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

Пример 3: Найти все большие файлы (больше 10 МБ), кроме .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. Полезные нюансы

Краткая шпаргалка по синтаксису glob

  • * — любое количество любых символов (кроме разделителя /)
  • ** — любое количество любых папок (в Java работает)
  • ? — ровно один любой символ
  • [abc] — любой из символов a, b, c
  • [a-z] — любой символ из диапазона
  • {a,b,c} — любое из перечисленных значений (например, *.{png,jpg})

Примеры:

  • *.java — все ".java"-файлы в текущей папке
  • **/*.java — все ".java"-файлы во всех подпапках
  • file-??.log — файлы вроде "file-01.log", "file-AB.log"
  • [A-Z]*.txt — все ".txt"-файлы, начинающиеся с заглавной буквы

Производительность и утечки: закрывайте DirectoryStream!

Важно!

  • DirectoryStream и потоки из Files.find/Files.walk — это ресурсы, которые нужно закрывать.
  • Используйте try-with-resources:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
    for (Path entry : stream) {
        // ...
    }
}
  • Если не закрывать поток, может возникнуть утечка ресурсов (например, «слишком много открытых файлов»).
  • Для Files.find и Files.walk — обязательно вызывайте close() или используйте try-with-resources:
try (Stream<Path> stream = Files.find(...)) {
    stream.forEach(System.out::println);
}

7. Итоги и типичные ошибки

Ошибка №1: Использование glob-шаблона без понимания, что * не ищет рекурсивно. Для рекурсии используйте "**/*.java" или Files.walk.

Ошибка №2: Передача полного пути в matcher.matches() — обычно нужно передавать только имя файла (getFileName()).

Ошибка №3: Забыли закрыть DirectoryStream или Stream<Path> — получите утечку ресурсов.

Ошибка №4: Слишком сложные regex-шаблоны для простой фильтрации — используйте glob, если хватает.

Ошибка №5: Не исключили служебные папки (".git", "node_modules") — поиск становится медленным и «засоряется» лишними файлами.

1
Задача
JAVA 25 SELF, 39 уровень, 2 лекция
Недоступна
Аналитик данных: отбор критически важных отчётов
Аналитик данных: отбор критически важных отчётов
1
Задача
JAVA 25 SELF, 39 уровень, 2 лекция
Недоступна
Куратор галереи: инвентаризация изображений
Куратор галереи: инвентаризация изображений
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ