web-dev-qa-db-fra.com

Comment trouver des fichiers qui correspondent à une chaîne générique en Java?

Cela devrait être vraiment simple. Si j'ai une chaîne comme celle-ci:

_../Test?/sample*.txt
_

alors quel est un moyen généralement accepté d’obtenir une liste de fichiers correspondant à ce modèle? (Par exemple, il doit correspondre à _../Test1/sample22b.txt_ et _../Test4/sample-spiffy.txt_ mais pas à _../Test3/sample2.blah_ ou _../Test44/sample2.txt_)

J'ai jeté un œil à org.Apache.commons.io.filefilter.WildcardFileFilter et cela semble être la bonne bête, mais je ne sais pas comment l'utiliser pour rechercher des fichiers dans un chemin de répertoire relatif.

Je suppose que je peux chercher la source pour ant car elle utilise la syntaxe générique, mais il me manque quelque chose d'assez évident ici.

( edit : l'exemple ci-dessus n'est qu'un exemple de cas. Je cherche le moyen d'analyser les chemins généraux contenant des caractères génériques au moment de l'exécution. J'ai compris comment de le faire sur la suggestion de mmyers mais c’est un peu gênant. Sans compter que le JRE Java semble analyser automatiquement les jokers simples dans les arguments principaux (String []) à partir du même argument "save "moi le temps et les tracas ... Je suis juste content de ne pas avoir d'argument non-fichier dans le mélange.)

136
Jason S

Considérons DirectoryScanner d'Apache Ant:

DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes(new String[]{"**/*.Java"});
scanner.setBasedir("C:/Temp");
scanner.setCaseSensitive(false);
scanner.scan();
String[] files = scanner.getIncludedFiles();

Vous aurez besoin de référencer ant.jar (~ 1,3 Mo pour ant 1.7.1).

71
Misha

Essayez FileUtils à partir de méthodes Apache commons-io (méthodes listFiles et iterateFiles):

File dir = new File(".");
FileFilter fileFilter = new WildcardFileFilter("sample*.Java");
File[] files = dir.listFiles(fileFilter);
for (int i = 0; i < files.length; i++) {
   System.out.println(files[i]);
}

Pour résoudre votre problème avec les dossiers TestX, je commencerais par parcourir la liste des dossiers:

File[] dirs = new File(".").listFiles(new WildcardFileFilter("Test*.Java");
for (int i=0; i<dirs.length; i++) {
   File dir = dirs[i];
   if (dir.isDirectory()) {
       File[] files = dir.listFiles(new WildcardFileFilter("sample*.Java"));
   }
}

C'est une solution assez brutale mais qui devrait bien fonctionner. Si cela ne correspond pas à vos besoins, vous pouvez toujours utiliser le RegexFileFilter .

114
Vladimir

Voici des exemples de listage de fichiers par modèle optimisé par Java 7 nioglobbing et Java 8 lambdas:

    try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
            Paths.get(".."), "Test?/sample*.txt")) {
        dirStream.forEach(path -> System.out.println(path));
    }

ou

    PathMatcher pathMatcher = FileSystems.getDefault()
        .getPathMatcher("regex:Test./sample\\w+\\.txt");
    try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
            new File("..").toPath(), pathMatcher::matches)) {
        dirStream.forEach(path -> System.out.println(path));
    }
47
Vadzim

Vous pouvez convertir votre chaîne générique en une expression régulière et l'utiliser avec la méthode matches de String. Suivant votre exemple:

String original = "../Test?/sample*.txt";
String regex = original.replace("?", ".?").replace("*", ".*?");

Cela fonctionne pour vos exemples:

Assert.assertTrue("../Test1/sample22b.txt".matches(regex));
Assert.assertTrue("../Test4/sample-spiffy.txt".matches(regex));

Et contre-exemples:

Assert.assertTrue(!"../Test3/sample2.blah".matches(regex));
Assert.assertTrue(!"../Test44/sample2.txt".matches(regex));
27
Fabian Steeg

Cela ne vous aidera peut-être pas pour l'instant, mais JDK 7 a pour but de faire correspondre les noms de fichier glob et regex dans le cadre de "Autres fonctionnalités NIO".

18

Depuis Java 8, vous pouvez utiliser la méthode Files#find directement à partir de Java.nio.file.

public static Stream<Path> find(Path start,
                                int maxDepth,
                                BiPredicate<Path, BasicFileAttributes> matcher,
                                FileVisitOption... options)

Exemple d'utilisation

Files.find(startingPath,
           Integer.MAX_VALUE,
           (path, basicFileAttributes) -> path.toFile().getName().matches(".*.pom")
);
18
Grzegorz Gajos

La bibliothèque générique fait efficacement la correspondance des noms de fichiers glob et regex:

http://code.google.com/p/wildcard/

La mise en œuvre est succincte - JAR n’est que de 12,9 kilo-octets.

12
NateS

Manière simple sans utiliser aucune importation externe est d'utiliser cette méthode

J'ai créé des fichiers csv portant les noms billing_201208.csv, billing_201209.csv, billing_201210.csv et tout se passe bien.

La sortie sera la suivante si les fichiers listés ci-dessus existent

found billing_201208.csv
found billing_201209.csv
found billing_201210.csv
 // Use Import -> import Java.io.File 
 Public static void main (String [] args) {
 String pathToScan = "."; 
 String target_file; // fileThatYouWantToFilter 
 Fichier folderToScan = new Fichier (pathToScan); 
 
    File[] listOfFiles = folderToScan.listFiles();

     for (int i = 0; i < listOfFiles.length; i++) {
            if (listOfFiles[i].isFile()) {
                target_file = listOfFiles[i].getName();
                if (target_file.startsWith("billing")
                     && target_file.endsWith(".csv")) {
                //You can add these files to fileList by using "list.add" here
                     System.out.println("found" + " " + target_file); 
                }
           }
     }    
}

10
Umair Aziz

Comme indiqué dans une autre réponse, la bibliothèque générique fonctionne à la fois pour les correspondances de noms de fichiers globes et regex: http://code.google.com/p/wildcard/

J'ai utilisé le code suivant pour faire correspondre les modèles globaux, notamment absolu et relatif sur les systèmes de fichiers de style * nix:

String filePattern = String baseDir = "./";
// If absolute path. TODO handle windows absolute path?
if (filePattern.charAt(0) == File.separatorChar) {
    baseDir = File.separator;
    filePattern = filePattern.substring(1);
}
Paths paths = new Paths(baseDir, filePattern);
List files = paths.getFiles();

J'ai passé un certain temps à essayer d'obtenir les méthodes FileUtils.listFiles de la bibliothèque Apache commons io (voir la réponse de Vladimir), mais sans succès (je réalise/pense maintenant qu'il ne peut gérer que les modèles correspondant à un répertoire ou un fichier à la fois). .

De plus, l'utilisation de filtres de regex (voir la réponse de Fabian) pour traiter des modèles de glob globaux de type absolus fournis par l'utilisateur sans effectuer de recherche dans l'ensemble du système de fichiers nécessiterait un prétraitement du glob fourni afin de déterminer le plus grand préfixe non regex/glob.

Bien sûr, Java 7 peut gérer correctement les fonctionnalités demandées, mais malheureusement, je suis coincé avec Java 6 pour le moment. La bibliothèque est relativement minuscule et mesure 13,5 Ko.

Note aux relecteurs: j'ai tenté d'ajouter ce qui précède à la réponse existante en mentionnant cette bibliothèque, mais la modification a été rejetée. Je n'ai pas assez de rep pour ajouter ceci comme commentaire non plus. N'y a-t-il pas un meilleur moyen ...

6
Oliver Coleman

Vous devriez pouvoir utiliser le WildcardFileFilter. Utilisez simplement System.getProperty("user.dir") pour obtenir le répertoire de travail. Essaye ça:

public static void main(String[] args) {
File[] files = (new File(System.getProperty("user.dir"))).listFiles(new WildcardFileFilter(args));
//...
}

Il n'est pas nécessaire de remplacer * par [.*], en supposant que le filtre générique utilise Java.regex.Pattern. Je n'ai pas testé cela, mais j'utilise constamment des modèles et des filtres de fichiers.

5
Anonymous

Le filtre Apache est conçu pour itérer des fichiers dans un répertoire connu. Pour autoriser également les caractères génériques dans le répertoire, vous devez diviser le chemin sur '\' ou '/' et filtrer chaque partie séparément.

3
Michael Myers

Pourquoi ne pas utiliser quelque chose comme:

File myRelativeDir = new File("../../foo");
String fullPath = myRelativeDir.getCanonicalPath();
Sting wildCard = fullPath + File.separator + "*.txt";

// now you have a fully qualified path

Dans ce cas, vous n’aurez plus à vous soucier des chemins relatifs et vous pourrez utiliser les caractères génériques nécessaires.

0
Elijah

Méthode Util:

public static boolean isFileMatchTargetFilePattern(final File f, final String targetPattern) {
        String regex = targetPattern.replace(".", "\\.");  //escape the dot first
        regex = regex.replace("?", ".?").replace("*", ".*");
        return f.getName().matches(regex);

    }

jUnit Test:

@Test
public void testIsFileMatchTargetFilePattern()  {
    String dir = "D:\\repository\\org\my\\modules\\mobile\\mobile-web\\b1605.0.1";
    String[] regexPatterns = new String[] {"_*.repositories", "*.pom", "*-b1605.0.1*","*-b1605.0.1", "mobile*"};
    File fDir = new File(dir);
    File[] files = fDir.listFiles();

    for (String regexPattern : regexPatterns) {
        System.out.println("match pattern [" + regexPattern + "]:");
        for (File file : files) {
            System.out.println("\t" + file.getName() + " matches:" + FileUtils.isFileMatchTargetFilePattern(file, regexPattern));
        }
    }
}

Sortie:

match pattern [_*.repositories]:
    mobile-web-b1605.0.1.pom matches:false
    mobile-web-b1605.0.1.war matches:false
    _remote.repositories matches:true
match pattern [*.pom]:
    mobile-web-b1605.0.1.pom matches:true
    mobile-web-b1605.0.1.war matches:false
    _remote.repositories matches:false
match pattern [*-b1605.0.1*]:
    mobile-web-b1605.0.1.pom matches:true
    mobile-web-b1605.0.1.war matches:true
    _remote.repositories matches:false
match pattern [*-b1605.0.1]:
    mobile-web-b1605.0.1.pom matches:false
    mobile-web-b1605.0.1.war matches:false
    _remote.repositories matches:false
match pattern [mobile*]:
    mobile-web-b1605.0.1.pom matches:true
    mobile-web-b1605.0.1.war matches:true
    _remote.repositories matches:false
0
Tony
Path testPath = Paths.get("C:\");

Stream<Path> stream =
                Files.find(testPath, 1,
                        (path, basicFileAttributes) -> {
                            File file = path.toFile();
                            return file.getName().endsWith(".Java");
                        });

// Print all files found
stream.forEach(System.out::println);
0
Anatoliy Shuba

Implémentez l'interface JDK FileVisitor. Voici un exemple http://wilddiary.com/list-files-matching-a-naming-pattern-Java/

0
Drona