1. Introduction
Lorsque vous travaillez avec des fichiers et des dossiers, il est souvent nécessaire de ne sélectionner que certains fichiers : par exemple, tous les fichiers ".java", toutes les images, tous les journaux à une date donnée. Pour cela, en Java (et pas seulement), on utilise le globbing (glob) et les expressions régulières (regex).
- Globbing — un moyen simple de décrire un motif de nom de fichier à l’aide de caractères spéciaux (*, ?, [], {}), comme dans la ligne de commande Linux ou Windows.
- Regex — un langage puissant d’expressions régulières pour des motifs complexes.
Exemples de motifs de globbing
- *.java — tous les fichiers avec l’extension ".java" dans le dossier courant.
- **/*.java — tous les fichiers ".java" dans tous les sous-dossiers (double astérisque — recherche récursive).
- *.{png,jpg} — tous les fichiers avec l’extension ".png" ou ".jpg".
- file-??.log — des fichiers comme "file-01.log", "file-AB.log" (deux caractères quelconques).
- [A-Z]*.txt — tous les fichiers ".txt" commençant par une lettre majuscule.
Le globbing est plus simple que regex et, le plus souvent, il suffit pour filtrer des fichiers.
Comparaison entre glob et regex
| Caractéristique | glob | regex |
|---|---|---|
| Simplicité | Très simple | Plus complexe, mais plus puissant |
| Symboles | |
Toutes les capacités de regex |
| Exemples | |
|
| Récursivité | |
Pas de prise en charge intégrée |
| Quand l’utiliser | Filtrage de fichiers | Vérifications complexes |
2. PathMatcher : filtrage de fichiers par motif
En Java NIO (java.nio.file), pour filtrer les fichiers selon un motif, on utilise l’interface PathMatcher. On peut l’obtenir via FileSystems.getDefault().getPathMatcher(...).
Syntaxe
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.java");
- "glob:*.java" — motif de globbing.
- "regex:.*\\.java" — motif d’expression régulière.
Exemple : filtrer les fichiers dans un dossier
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() vérifie généralement uniquement le nom de fichier — passez entry.getFileName(), et non le chemin complet.
- Pour une recherche récursive, utilisez Files.walk() ou Files.find().
Files.walk() est une méthode NIO2 qui renvoie un Stream<Path> avec tous les fichiers et dossiers dans le répertoire indiqué, de manière récursive, y compris les sous-dossiers. À la différence de DirectoryStream, qui montre uniquement le contenu d’un seul dossier, Files.walk() permet de travailler avec l’arborescence via l’API Stream.
Exemple d’utilisation :
import java.nio.file.*;
import java.util.stream.Stream;
Path start = Paths.get("src");
try (Stream<Path> stream = Files.walk(start)) { // tous les sous-répertoires de manière récursive
stream.filter(Files::isRegularFile)
.forEach(System.out::println);
}
Exemple avec regex
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("regex:.*\\.(png|jpg)");
3. Files.newDirectoryStream : filtrage lors de l’affichage d’un dossier
La méthode Files.newDirectoryStream() permet de filtrer directement les fichiers selon un motif ou à l’aide de votre propre filtre.
Filtrage par motif glob
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
for (Path entry : stream) {
System.out.println(entry);
}
}
Le second paramètre est un motif glob (sans le préfixe "glob:").
Filtrage avec DirectoryStream.Filter
Si vous avez besoin d’une logique plus complexe (par exemple, filtrage par taille, date, exclusion de dossiers), utilisez DirectoryStream.Filter<Path> :
DirectoryStream.Filter<Path> filter = path -> {
// Exemple : seulement des fichiers, pas des dossiers, et pas .git ni 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 : sélection puissante avec BiPredicate
Si vous devez rechercher des fichiers selon des critères complexes (par exemple, par date, taille, nom, de manière récursive), utilisez Files.find.
Syntaxe
Stream<Path> stream = Files.find(
startDir, // où chercher
maxDepth, // profondeur (Integer.MAX_VALUE — récursif)
(path, attrs) -> {
// path — chemin du fichier
// attrs — attributs du fichier (taille, date, etc.)
return path.getFileName().toString().endsWith(".log")
&& attrs.size() > 1024; // uniquement des logs volumineux
}
);
stream.forEach(System.out::println);
- Le deuxième paramètre est la profondeur maximale de recherche.
- Le troisième — BiPredicate<Path, BasicFileAttributes> : renvoie true si le fichier correspond.
Exemple : exclure les dossiers .git et node_modules
Stream<Path> stream = Files.find(
Paths.get("."),
Integer.MAX_VALUE,
(path, attrs) -> {
String name = path.getFileName().toString();
// Exclure les dossiers .git et node_modules
if (name.equals(".git") || name.equals("node_modules")) return false;
// Uniquement les fichiers .log de taille supérieure à 1 Mo
return name.endsWith(".log") && attrs.size() > 1024 * 1024;
}
);
stream.forEach(System.out::println);
5. Pratique
Exemple 1 : Trouver tous les fichiers .log, sauf .git et node_modules
Files.walk(Paths.get("."))
.filter(path -> {
String name = path.getFileName().toString();
// Exclure les dossiers
if (name.equals(".git") || name.equals("node_modules")) return false;
// Uniquement les fichiers .log
return name.endsWith(".log");
})
.forEach(System.out::println);
Exemple 2 : Trouver toutes les images créées après une date donnée
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);
Exemple 3 : Trouver tous les gros fichiers (plus de 10 Mo), sauf .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. Détails utiles
Aide-mémoire rapide de la syntaxe glob
- * — n’importe quel nombre de caractères (sauf le séparateur /)
- ** — n’importe quel nombre de sous-dossiers (fonctionne en Java)
- ? — exactement un caractère quelconque
- [abc] — l’un des caractères a, b, c
- [a-z] — tout caractère de l’intervalle
- {a,b,c} — l’une des valeurs listées (par exemple, *.{png,jpg})
Exemples :
- *.java — tous les fichiers ".java" dans le dossier courant
- **/*.java — tous les fichiers ".java" dans tous les sous-dossiers
- file-??.log — des fichiers comme "file-01.log", "file-AB.log"
- [A-Z]*.txt — tous les fichiers ".txt" commençant par une lettre majuscule
Performances et fuites : fermez DirectoryStream !
Important !
- DirectoryStream et les flux renvoyés par Files.find/Files.walk — ce sont des ressources qu’il faut fermer.
- Utilisez le try-with-resources :
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.java")) {
for (Path entry : stream) {
// ...
}
}
- Si vous ne fermez pas le flux, une fuite de ressources peut survenir (par exemple, « trop de fichiers ouverts »).
- Pour Files.find et Files.walk — appelez impérativement close() ou utilisez try-with-resources :
try (Stream<Path> stream = Files.find(...)) {
stream.forEach(System.out::println);
}
7. Conclusion et erreurs courantes
Erreur n° 1 : Utiliser un motif glob sans comprendre que * n’effectue pas de recherche récursive. Pour la récursivité, utilisez "**/*.java" ou Files.walk.
Erreur n° 2 : Passer le chemin complet à matcher.matches() — il faut généralement passer uniquement le nom de fichier (getFileName()).
Erreur n° 3 : Oublier de fermer DirectoryStream ou Stream<Path> — cela provoque une fuite de ressources.
Erreur n° 4 : Des motifs regex trop complexes pour un filtrage simple — utilisez glob si cela suffit.
Erreur n° 5 : Ne pas exclure les dossiers système (".git", "node_modules") — la recherche devient lente et est « polluée » par des fichiers superflus.
GO TO FULL VERSION