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

Globbing/PathMatcher, DirectoryStream.Filter

JAVA 25 SELF
Nível 39 , Lição 2
Disponível

1. Introdução

Quando você trabalha com arquivos e pastas, muitas vezes precisa selecionar apenas determinados arquivos: por exemplo, todos os arquivos ".java", todas as imagens, todos os logs de uma determinada data. Para isso, em Java (e não só) usa‑se o globbing (glob) e as expressões regulares (regex).

  • Globbing — uma forma simples de descrever um padrão de nome de arquivo usando caracteres especiais (*, ?, [], {}), como no terminal do Linux ou no Windows.
  • Regex — uma linguagem poderosa de expressões regulares para padrões complexos.

Exemplos de padrões glob

  • *.java — todos os arquivos com extensão ".java" na pasta atual.
  • **/*.java — todos os arquivos ".java" em todas as subpastas (duas estrelas — busca recursiva).
  • *.{png,jpg} — todos os arquivos com extensão ".png" ou ".jpg".
  • file-??.log — arquivos como "file-01.log", "file-AB.log" (dois caracteres quaisquer).
  • [A-Z]*.txt — todos os arquivos ".txt" que começam com letra maiúscula.

Globbing é mais simples do que regex e, na maioria dos casos, é suficiente para filtrar arquivos.

Comparação entre glob e regex

Característica glob regex
Simplicidade Muito simples Mais complexo, porém mais poderoso
Símbolos
*, ?, [], {}
Todos os recursos de regex
Exemplos
*.java
.*\.java
Recursividade
**/*.java
Sem suporte embutido
Quando usar Filtragem de arquivos Validações complexas

2. PathMatcher: filtragem de arquivos por padrão

No Java NIO (java.nio.file) utiliza‑se a interface PathMatcher para filtrar arquivos por padrão. Ela pode ser obtida via FileSystems.getDefault().getPathMatcher(...).

Sintaxe

PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
  • "glob:*.java" — padrão glob.
  • "regex:.*\\.java" — padrão de expressão regular.

Exemplo: filtragem de arquivos em uma pasta

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

Importante:

  • matcher.matches() normalmente verifica apenas o nome do arquivo — passe entry.getFileName(), e não o caminho completo.
  • Para busca recursiva, use Files.walk() ou Files.find().

Files.walk() — método do NIO2 que retorna um Stream<Path> com todos os arquivos e pastas no diretório informado, de forma recursiva, incluindo subpastas. Diferentemente de DirectoryStream, que exibe apenas o conteúdo de uma única pasta, Files.walk() permite trabalhar com a árvore de diretórios via Stream API.

Exemplo de uso:

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

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

Exemplo com regex

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

3. Files.newDirectoryStream: filtragem ao listar uma pasta

O método Files.newDirectoryStream() permite filtrar arquivos imediatamente por padrão ou com um filtro próprio.

Filtragem por padrão glob

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

O segundo parâmetro é um padrão glob (sem o prefixo "glob:").

Filtragem com DirectoryStream.Filter

Se precisar de uma lógica mais complexa (por exemplo, filtragem por tamanho, data, exclusão de pastas), use DirectoryStream.Filter<Path>:

DirectoryStream.Filter<Path> filter = path -> {
    // Exemplo: apenas arquivos (não pastas) e não .git nem 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: seleção avançada com BiPredicate

Se você precisa procurar arquivos por condições complexas (por exemplo, por data, tamanho, nome, de forma recursiva), use Files.find.

Sintaxe

Stream<Path> stream = Files.find(
    startDir, // onde procurar
    maxDepth, // profundidade (Integer.MAX_VALUE — recursivo)
    (path, attrs) -> {
        // path — caminho do arquivo
        // attrs — atributos do arquivo (tamanho, data etc.)
        return path.getFileName().toString().endsWith(".log")
            && attrs.size() > 1024; // apenas logs grandes
    }
);

stream.forEach(System.out::println);
  • O segundo parâmetro é a profundidade máxima da busca.
  • O terceiro é um BiPredicate<Path, BasicFileAttributes>: retorna true se o arquivo atender ao critério.

Exemplo: excluir as pastas .git e node_modules

Stream<Path> stream = Files.find(
    Paths.get("."),
    Integer.MAX_VALUE,
    (path, attrs) -> {
        String name = path.getFileName().toString();
        // Excluímos as pastas .git e node_modules
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Apenas arquivos .log maiores que 1 MB
        return name.endsWith(".log") && attrs.size() > 1024 * 1024;
    }
);
stream.forEach(System.out::println);

5. Prática

Exemplo 1: encontrar todos os arquivos .log, exceto .git e node_modules

Files.walk(Paths.get("."))
    .filter(path -> {
        String name = path.getFileName().toString();
        // Excluímos as pastas
        if (name.equals(".git") || name.equals("node_modules")) return false;
        // Apenas arquivos .log
        return name.endsWith(".log");
    })
    .forEach(System.out::println);

Exemplo 2: encontrar todas as imagens criadas após uma determinada data

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

Exemplo 3: encontrar todos os arquivos grandes (maiores que 10 MB), exceto .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. Detalhes úteis

Guia rápido da sintaxe do glob

  • * — qualquer quantidade de quaisquer caracteres (exceto o separador /)
  • ** — qualquer quantidade de pastas (funciona em Java)
  • ? — exatamente um caractere qualquer
  • [abc] — qualquer um dos caracteres a, b, c
  • [a-z] — qualquer caractere do intervalo
  • {a,b,c} — qualquer um dos valores listados (por exemplo, *.{png,jpg})

Exemplos:

  • *.java — todos os arquivos ".java" na pasta atual
  • **/*.java — todos os arquivos ".java" em todas as subpastas
  • file-??.log — arquivos como "file-01.log", "file-AB.log"
  • [A-Z]*.txt — todos os arquivos ".txt" que começam com letra maiúscula

Desempenho e vazamentos: feche o DirectoryStream!

Importante!

  • DirectoryStream e os streams de Files.find/Files.walk — são recursos que precisam ser fechados.
  • Use try-with-resources:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
    for (Path entry : stream) {
        // ...
    }
}
  • Se o stream não for fechado, pode ocorrer vazamento de recursos (por exemplo, “muitos arquivos abertos”).
  • Para Files.find e Files.walk — chame close() obrigatoriamente ou use try-with-resources:
try (Stream<Path> stream = Files.find(...)) {
    stream.forEach(System.out::println);
}

7. Conclusão e erros comuns

Erro nº 1: Usar um padrão glob sem entender que * não pesquisa recursivamente. Para recursão, use "**/*.java" ou Files.walk.

Erro nº 2: Passar o caminho completo para matcher.matches() — normalmente deve‑se passar apenas o nome do arquivo (getFileName()).

Erro nº 3: Esquecer de fechar DirectoryStream ou Stream<Path> — isso causa vazamento de recursos.

Erro nº 4: Padrões regex demasiadamente complexos para uma filtragem simples — use glob quando for suficiente.

Erro nº 5: Não excluir pastas de sistema (".git", "node_modules") — a busca fica lenta e “poluída” por arquivos desnecessários.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION