Java – trouver un fichier dans une arborescence

Imaginons qu’on cherche à vérifier si un fichier est présent dans une arborescence de dossiers. Il existe plusieurs façons de procéder. Si on connait le chemin du dit fichier, on peut créer une instance d’objet File avec ce chemin et utiliser la méthode exist(). C’est simple et rapide. Mais si on ne connait pas le chemin du fichier, et si on n’est pas non plus certain de l’extension du fichier?! Il faut alors se tourner vers la récursivité et les expressions régulières!

Au départ, j’étais parti sur une approche assez basique, fonctionnelle mais au final peu performante quand il s’agissait de parcourir une arborescence assez importante (plusieurs milliers de fichiers) :

	public static String findByRegex(File folder, Pattern filename){
		String path = null;
		if(folder.isDirectory()){
			List<File> files = Arrays.asList(folder.listFiles());
			Iterator<File> fileIterator = files.iterator();
			while(fileIterator.hasNext() &&  path==null){
				path = findByRegex(fileIterator.next(), filename);
			}
		} else {
			Matcher matcher = filename.matcher(folder.getName().toLowerCase());
			if (matcher.find()) {
				path = folder.getAbsolutePath();
			}
		}
		return path;
	}

Après quelques petites recherches, j’ai trouvé une solution alternative plus performante : utiliser les Filters. En effet, la méthode File.listFiles() peut prendre en paramètre un objet de type FileFilter ou FilenameFilter. Ces filtres font appel à la méthode accept() pour déterminer si le fichier parcouru doit être retourné dans la liste ou non. Dans le cas d’utilisation présent, la condition pour que le fichier soit retourné est que son nom doit correspondre à un pattern spécifique :

	public static String findByFilter(File parent, Pattern filename) {
		String path = null;
		if (parent.isDirectory()) {
			RegexFileFilter filter = new RegexFileFilter(filename);
			File[] children = parent.listFiles(filter);
			if (children.length == 1) {
				path = children[0].getAbsolutePath();
			} else {
				List<File> files = Arrays.asList(parent.listFiles());
				Iterator<File> fileIterator = files.iterator();
				while(fileIterator.hasNext() && path == null) {
					path = findByFilter(fileIterator.next(), filename);
				}
			}
		}

		return path;
	}

La classe RegexFileFilter implémente l’interface FileFilter et comprend la propriété pattern, soit le pattern que le fichier doit matcher pour être accepté:

public class RegexFileFilter implements FileFilter {

	/** The regular expression pattern that will be used to match filenames */
    private final Pattern pattern;

    /**
     * Construct a new regular expression filter.
     *
     * @param p regular string expression to match
     * @throws IllegalArgumentException if the pattern is null
     */
    public RegexFileFilter(Pattern p) {
        if (p == null) {
            throw new IllegalArgumentException("Pattern is missing");
        }

        this.pattern = p;
    }

	@Override
	public boolean accept(File pathname) {
		return (pattern.matcher(pathname.getName()).matches());
	}

}

Voilà! :)

Il est possible d’adapter un peu cette solution en fonction des besoins. Par exemple si on souhaite trouver tous les fichiers qui correspondent au pattern, on peut remplacer la variable path par une liste et la remplir au fur et à mesure qu’on matche des fichiers. Il faut alors retirer la seconde condition dans la boucle while pour pouvoir parcourir l’ensemble de l’arborescence :) Il faudra aussi instancier la liste avant de faire appel à la méthode findByFilter et la passer en paramètre de celle-ci.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *