Як знайти файли, які відповідають рядовій послідовності на Java?


157

Це повинно бути справді просто. Якщо у мене є така струна:

../Test?/sample*.txt

то який загальноприйнятий спосіб отримати список файлів, які відповідають цій схемі? (Наприклад , він повинен відповідати ../Test1/sample22b.txtі , ../Test4/sample-spiffy.txtале не ../Test3/sample2.blahабо ../Test44/sample2.txt)

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

Я припускаю, що я можу шукати джерело мурашки, оскільки він використовує синтаксис підстановки, але я, мабуть, пропускаю щось досить очевидне.

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


2
Це оболонка, що аналізує підстановку, а не Java. Ви можете уникнути їх, але точний формат залежить від вашої системи.
Майкл Майерс

2
Ні це не так. Windows не розбирає * підстановку. Я перевірив це, запустивши той самий синтаксис на фіктивний пакетний файл і роздрукувавши аргумент №1, який був Test / *. Obj, вказуючи на каталог, повний файлів .obj. Він друкує "Test / *. Obj". Здається, Java робить щось дивне.
Джейсон S

Ага, ти маєш рацію; майже всі вбудовані команди оболонки розширюють підстановку, але сама оболонка цього не робить. У будь-якому випадку, ви можете просто поставити аргумент у лапки, щоб уникнути розбору Java-символів: java MyClass "Test / *. Obj"
Майкл Майєрс

3
Через 6 років, для тих, хто ненавидить прокручування і хоче рішення Java> = 7 з нульовим відхиленням, див. І підтвердити відповідь нижче за допомогою @Vadzim, або багатослівної пори / переносу
earcam

Відповіді:


81

Розгляньте 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 Мб для мурашника 1.7.1).


1
відмінно! btw, scanner.getIncludedDirectories () робить те саме, якщо вам потрібні каталоги. (getIncludedFiles не працюватиме)
Tilman Hausherr

1
Проект підстановки на Github також працює як шарм: github.com/EsotericSoftware/wildcard
Мореакі

1
@Moreaki, що належить як окрема відповідь, а не коментар
Jason S

Це саме те саме DirectoryScannerє у сплетіння (241Kb). Що менше ant.jar(1.9Mb).
Верхаген

Це працює. Але це здається надзвичайно повільним порівняно lsз тим самим шаблоном файлів (мілісекунди з використанням ls <pattern>хвилин проти використання DirectoryScanner) ...
dokaspar

121

Спробуйте 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 .


2
Гаразд, тепер ви зрозуміли, де саме був Джейсон С, коли він розмістив запитання.
Майкл Майерс

не зовсім. Існує також RegexFileFilter, який можна використовувати (але особисто мені цього не потрібно було робити).
Володимир

57

Нижче наведені приклади списку файлів за шаблоном харчування від Java 7 NiO універсалізації та Java 8 лямбда:

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

13
АбоFiles.walk(Paths.get("..")).filter(matcher::matches).forEach(System.out::println);
амебе

@Qstnr_La, так, за винятком допоміжних лямбдів та посилань на методи.
Вадим

29

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

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

3
Це не працюватиме для файлів, які містять спеціальні символи регулярного
виразів

Я використовував 'String regex = "^" + s.replace ("?", ".?"). Substitute (" ", ". ?") + "$"' (Зірочки чомусь зникли в моєму коментарі. ..)
Jouni Aro

2
Чому замінити * на '. *? ? public static boolean isFileMatchTargetFilePattern (final File f, final String targetPattern) {`` String regex = targetPattern.replace (".", "\\."); ` regex = regex.replace("?", ".?").replace("* ", ".*"); return f.getName().matches(regex); }
Тоні

Оскільки ОП вимагає "загальних доріжок, що містять символи", вам доведеться навести більше спеціальних символів. Я вважаю за краще використовувати Pattern.quote:StringBuffer regexBuffer = ...; Matcher matcher = Pattern.compile("(.*?)([*?])").matcher(original); while (matcher.find()) { matcher.appendReplacement(regexBuffer, (Pattern.quote(matcher.group(1)) + (matcher.group(2).equals("*") ? ".*?" : ".?")).replace("\\", "\\\\").replace("$", "\\$")); } matcher.appendTail(regexBuffer);
EndlosSchleife

Додаток: "?" позначає обов'язковий знак, тому його слід замінити .замість .?.
EndlosSchleife

23

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

1
Чи можете ви накласти приклад, щоб сказати, надрукуйте шлях першого збігу, що зберігається в потоці?
jxramos

18

Можливо, вам зараз не допоможуть, але JDK 7 призначений для того, щоб ім'я файлів glob і regex відповідало як частина "Більше функцій NIO".


3
На Java 7: Files.newDirectoryStream (шлях, глобальний зразок)
Пат Німейер

13

Бібліотека підстановок ефективно поєднує ім'я файлів глобус та регекс:

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

Реалізація є короткою - JAR складає всього 12,9 кілобайт.


2
Єдиним недоліком є ​​те, що його немає в Maven Central
yegor256

3
Це ОС, ідіть і покладіть його на Maven Central. :)
NateS

10

Простий спосіб без використання зовнішнього імпорту - це використання цього методу

Я створив CSV файли з ім'ям billing_201208.csv, billing_201209.csv, billing_201210.csv, і це виглядає як добре працює.

Вихід буде наступним, якщо існують перелічені вище файли

found billing_201208.csv
found billing_201209.csv
found billing_201210.csv

    // Використовувати Імпорт -> імпорт java.io.File
        public static void main (String [] args) {
        String pathToScan = ".";
        Рядок target_file; // файлThatYouWantToFilter
        Файл файлуToScan = новий файл (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); 
                }
           }
     }    
}


6

Як опубліковано в іншій відповіді, бібліотека підказок працює для того, щоб відповідати ім'я файлів глобусу та регексу: http://code.google.com/p/wildcard/

Я використовував наступний код для узгодження глобальних моделей, включаючи абсолютні та відносні файлові системи * 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 (див. Відповідь Володимира), щоб зробити це, але не мав успіху (я розумію, зараз / думаю, що він може обробляти лише шаблон, що відповідає одному каталогу або файлу за один раз) .

Крім того, використання фільтрів регулярних виразів (див. Відповідь Фабіана) для обробки довільних шаблонів глобального типу, що надаються користувачем, без пошуку всієї файлової системи потребує певної попередньої обробки поданого глоба, щоб визначити найбільший префікс не-регулярного вибору / глобуса.

Звичайно, Java 7 може прекрасно обробляти потрібну функціональність, але, на жаль, я поки що затримався з Java 6. Бібліотека порівняно незначна в розмірі 13,5 кб.

Примітка для рецензентів: я намагався додати вищезазначене до існуючої відповіді, згадавши про цю бібліотеку, але редагування було відхилено. У мене недостатньо представників, щоб додати це також як коментар. Хіба немає кращого способу ...


Чи плануєте ви перенести свій проект кудись ще? Дивіться code.google.com/p/support/wiki/ReadOnlyTransition
Люк М

1
'це не мій проект, і, схоже, його вже перенесли: github.com/EsotericSoftware/wildcard
Олівер Коулман

5

Ви повинні мати можливість використовувати 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. Я цього не перевіряв, але постійно використовую шаблони та фільтри для файлів.



3

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


1
Це спрацювало. Це було трохи прикро, але не особливо схильне до проблем. Однак я з нетерпінням чекаю можливостей JDK7 для глобальної відповідності.
Джейсон S

0

Чому б не використати щось таке:

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

// now you have a fully qualified path

Тоді вам не доведеться турбуватися про відносні шляхи, і ви можете робити свою підводку по мірі необхідності.


1
Тому що відносна стежка може мати і магістральні символи.
Jason S


0

Метод 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
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

не можна просто використовувати пошук тексту за допомогою шляхів файлової системи; інакше foo/bar.txtматчі, foo?bar.txtі це невірно
Jason S

Джейсон Я використав file.getName (), який не містить шляху.
Тоні

тоді це не працює для прикладу, який я дав:../Test?/sample*.txt
Jason S

0
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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.