Как найти файлы, соответствующие строке подстановки в Java?

Это должно быть очень просто. Если у меня есть такая строка:

../Test?/sample*.txt

то что является общепринятым способом получения списка файлов, соответствующих этому шаблону? (например, он должен соответствовать ../Test1/sample22b.txt и ../Test4/sample-spiffy.txt, но не ../Test3/sample2.blah или ../Test44/sample2.txt)

Я взглянул на org.apache.commons.io.filefilter.WildcardFileFilter, и это похоже на правильное зверь, но я не уверен, как его использовать для поиск файлов в относительном пути к каталогам.

Я полагаю, что я могу посмотреть источник для ant, поскольку он использует синтаксис подстановочных знаков, но мне, должно быть, не хватает чего-то довольно очевидного здесь.

(edit): приведенный выше пример был всего лишь примером. Я ищу способ разобрать общие пути, содержащие подстановочные знаки во время выполнения. Я выяснил, как это сделать на основе предложения mmyers но это немного раздражает. Не говоря уже о том, что java JRE, похоже, автоматически анализирует простые подстановочные знаки в основном (аргументы String []) из одного аргумента, чтобы "сохранить" мне время и хлопоты... Я просто рад, что я не было аргументов без файла в миксе.)

Ответ 1

Рассмотрим DirectoryScanner из Apache Ant:

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

Вам понадобится ссылка ant.jar(~ 1,3 МБ для ant 1.7.1).

Ответ 2

Попробуйте FileUtils из Apache commons-io (listFiles и 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]);
}

Чтобы решить вашу проблему с папками TestX, я бы сначала перебрал список папок:

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

Довольно грубое решение, но оно должно работать нормально. Если это не соответствует вашим потребностям, вы всегда можете использовать RegexFileFilter.

Ответ 3

Вот примеры перечисления файлов по шаблону на основе Java 7 nio globbing и Java 8 lambdas:

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

или же

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

Ответ 4

Вы можете преобразовать свою подстановочную строку в регулярное выражение и использовать ее с помощью метода String matches. Следуя вашему примеру:

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

Это работает для ваших примеров:

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

И встречные примеры:

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

Ответ 5

Не могу помочь вам прямо сейчас, но JDK 7 предназначен для сопоставления имени файла glob и regex в разделе "Дополнительные возможности NIO".

Ответ 6

Начиная с Java 8 вы можете использовать метод Files#find непосредственно из java.nio.file.

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

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

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

Ответ 7

Библиотека подстановок эффективно выполняет сопоставление имени файла glob и regex:

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

Реализация лаконична - JAR составляет всего 12,9 килобайта.

Ответ 8

Простой способ без использования внешнего импорта - использовать этот метод

Я создал csv файлы с именем billing_201208.csv, billing_201209.csv, billing_201210.csv, и похоже, что он работает нормально.

Вывод будет следующим, если существуют файлы, перечисленные выше

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
        File folderToScan = new File(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); 
                }
           }
     }    
}

Ответ 9

Как указано в другом ответе, библиотека подстановочных знаков работает для соответствия имени файла glob и regex: http://code.google.com/p/wildcard/

Я использовал следующий код для сопоставления шаблонов glob, включая абсолютные и относительные файловые системы в стиле * 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();

Я потратил некоторое время, пытаясь получить методы FileUtils.listFiles в библиотеке Apache commons io (см. ответ Владимира), чтобы сделать это, но не имел успеха (теперь я понимаю/думаю, что он может обрабатывать только шаблон, соответствующий одному каталогу или файлу в время).

Кроме того, использование регулярных фильтров (см. ответ Фабиана) для обработки произвольных пользовательских абсолютных шаблонов типа glob без поиска всей файловой системы потребует некоторой предварительной обработки поставляемого glob для определения самого большого префикса non-regex/glob.

Конечно, Java 7 может отлично справиться с запрошенной функциональностью, но, к сожалению, сейчас я придерживаюсь Java 6. Библиотека сравнительно незначительна при размере 13,5 тыс. Фунтов.

Примечание для рецензентов: я попытался добавить вышеперечисленное к существующему ответу с упоминанием этой библиотеки, но изменение было отклонено. У меня недостаточно комментариев, чтобы добавить это как комментарий. Разве нет лучшего способа...

Ответ 10

Вы можете использовать WildcardFileFilter. Просто используйте System.getProperty("user.dir"), чтобы получить рабочий каталог. Попробуйте следующее:

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

Вам не нужно заменять * на [.*], предполагая, что фильтр подстановки использует java.regex.Pattern. Я не тестировал это, но я постоянно использую шаблоны и фильтры файлов.

Ответ 12

Фильтр Apache создан для итерации файлов в известном каталоге. Чтобы разрешить использование подстановочных знаков в каталоге, вам придется разделить путь на "\" или "/" и сделать фильтр по каждой части отдельно.

Ответ 13

Почему бы не использовать что-то вроде:

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

// now you have a fully qualified path

Тогда вам не придется беспокоиться об относительных путях и при необходимости использовать свои подстановочные знаки.

Ответ 15

Метод использования:

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

Вывод:

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

Ответ 16

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