Список всех файлов из каталога рекурсивно с Java - PullRequest
79 голосов
/ 29 марта 2010

У меня есть эта функция, которая рекурсивно печатает имена всех файлов в каталоге. Проблема в том, что мой код очень медленный, потому что он должен обращаться к удаленному сетевому устройству при каждой итерации.

Мой план - сначала рекурсивно загрузить все файлы из каталога, а затем пройти через все файлы с помощью регулярного выражения, чтобы отфильтровать все файлы, которые мне не нужны. У кого-нибудь есть лучшее предложение?

public static printFnames(String sDir){
  File[] faFiles = new File(sDir).listFiles();
  for(File file: faFiles){
    if(file.getName().matches("^(.*?)")){
      System.out.println(file.getAbsolutePath());
    }
    if(file.isDirectory()){
      printFnames(file.getAbsolutePath());
    }
  }
}

Это просто тест позже, я не собираюсь использовать подобный код, вместо этого я собираюсь добавить путь и дату модификации каждого файла, который соответствует расширенному регулярному выражению, в массив.

Ответы [ 18 ]

128 голосов
/ 29 марта 2010

Предполагая, что это фактический производственный код, который вы будете писать, я предлагаю использовать решение для такого рода уже решенных задач - Apache Commons IO , в частности FileUtils.listFiles(). Он обрабатывает вложенные каталоги, фильтры (по имени, времени модификации и т. Д.).

Например, для вашего регулярного выражения:

Collection files = FileUtils.listFiles(
  dir, 
  new RegexFileFilter("^(.*?)"), 
  DirectoryFileFilter.DIRECTORY
);

Это будет рекурсивно искать файлы, соответствующие регулярному выражению ^(.*?), возвращая результаты в виде коллекции.

Стоит отметить, что это будет не быстрее, чем сворачивание вашего собственного кода, оно делает то же самое - траление файловой системы в Java просто медленное. Разница в том, что версия Apache Commons не содержит ошибок.

54 голосов
/ 02 ноября 2015

В Java 8 это однострочник через Files.find() с произвольно большой глубиной (например, 999) и BasicFileAttributes из isRegularFile()

public static printFnames(String sDir) {
    Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println);
}

Чтобы добавить дополнительную фильтрацию, улучшите лямбду, например, все файлы jpg, измененные за последние 24 часа:

(p, bfa) -> bfa.isRegularFile()
  && p.getFileName().toString().matches(".*\\.jpg")
  && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
25 голосов
/ 20 июня 2014

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

Используется класс Java 7 NIO Path.

private List<String> getFileNames(List<String> fileNames, Path dir) {
    try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
        for (Path path : stream) {
            if(path.toFile().isDirectory()) {
                getFileNames(fileNames, path);
            } else {
                fileNames.add(path.toAbsolutePath().toString());
                System.out.println(path.getFileName());
            }
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
    return fileNames;
} 
18 голосов
/ 14 августа 2013

В Java 7 был представлен более быстрый способ перехода по дереву каталогов с помощью функций Paths и Files. Они намного быстрее, чем «старый» File способ.

Это будет код, позволяющий проходить и проверять имена путей с помощью регулярного выражения:

public final void test() throws IOException, InterruptedException {
    final Path rootDir = Paths.get("path to your directory where the walk starts");

    // Walk thru mainDir directory
    Files.walkFileTree(rootDir, new FileVisitor<Path>() {
        // First (minor) speed up. Compile regular expression pattern only one time.
        private Pattern pattern = Pattern.compile("^(.*?)");

        @Override
        public FileVisitResult preVisitDirectory(Path path,
                BasicFileAttributes atts) throws IOException {

            boolean matches = pattern.matcher(path.toString()).matches();

            // TODO: Put here your business logic when matches equals true/false

            return (matches)? FileVisitResult.CONTINUE:FileVisitResult.SKIP_SUBTREE;
        }

        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts)
                throws IOException {

            boolean matches = pattern.matcher(path.toString()).matches();

            // TODO: Put here your business logic when matches equals true/false

            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path path,
                IOException exc) throws IOException {
            // TODO Auto-generated method stub
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path path, IOException exc)
                throws IOException {
            exc.printStackTrace();

            // If the root directory has failed it makes no sense to continue
            return path.equals(rootDir)? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE;
        }
    });
}
13 голосов
/ 18 марта 2013

Быстрый способ получить содержимое каталога с помощью Java 7 NIO:

import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.FileSystems;
import java.nio.file.Path;

...

Path dir = FileSystems.getDefault().getPath( filePath );
DirectoryStream<Path> stream = Files.newDirectoryStream( dir );
for (Path path : stream) {
   System.out.println( path.getFileName() );
}
stream.close();
12 голосов
/ 29 марта 2010

Интерфейс Java для чтения содержимого папки файловой системы не очень производительный (как вы обнаружили). JDK 7 исправляет это с помощью совершенно нового интерфейса для такого рода вещей, который должен обеспечивать производительность на родном уровне для таких операций.

Основная проблема заключается в том, что Java выполняет собственный системный вызов для каждого отдельного файла. На интерфейсе с низкой задержкой это не так уж сложно, но в сети с даже умеренной задержкой это действительно добавляет. Если вы профилируете свой алгоритм выше, вы обнаружите, что большая часть времени тратится на надоедливый вызов isDirectory () - это потому, что вы выполняете туда-обратно для каждого отдельного вызова isDirectory (). Большинство современных ОС могут предоставлять такую ​​информацию, когда первоначально запрашивался список файлов / папок (в отличие от запроса каждого отдельного пути к файлу для его свойств).

Если вы не можете дождаться JDK7, одной из стратегий для устранения этой задержки является использование многопоточности и использование ExecutorService с максимальным количеством потоков для выполнения рекурсии. Это не очень хорошо (вам приходится иметь дело с блокировкой ваших структур выходных данных), но это будет намного быстрее, чем выполнение этого однопоточного.

Во всех ваших дискуссиях о подобных вещах я настоятельно рекомендую вам сравнить с лучшим, что вы могли бы сделать, используя нативный код (или даже сценарий командной строки, который делает примерно то же самое). Сказать, что обход структуры сети занимает час, на самом деле не так уж много значит. Рассказ о том, что вы можете сделать это за 7 секунд, но в Java это займет час, привлечет внимание людей.

5 голосов
/ 19 февраля 2015

это будет работать просто отлично ... и его рекурсивно

File root = new File("ROOT PATH");
for ( File file : root.listFiles())
{
    getFilesRecursive(file);
}


private static void getFilesRecursive(File pFile)
{
    for(File files : pFile.listFiles())
    {
        if(files.isDirectory())
        {
            getFilesRecursive(files);
        }
        else
        {
            // do your thing 
            // you can either save in HashMap and use it as
            // per your requirement
        }
    }
}
3 голосов
/ 20 мая 2015

Мне лично нравится эта версия FileUtils. Вот пример, который находит все mp3s или флаки в каталоге или любом из его подкаталогов:

String[] types = {"mp3", "flac"};
Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);
3 голосов
/ 30 апреля 2015

Это будет нормально работать

public void displayAll(File path){      
    if(path.isFile()){
        System.out.println(path.getName());
    }else{
        System.out.println(path.getName());         
        File files[] = path.listFiles();
        for(File dirOrFile: files){
            displayAll(dirOrFile);
        }
    }
}

1 голос
/ 08 марта 2013

Эта функция, вероятно, выведет все имя файла и его путь из своего каталога и его подкаталогов.

public void listFile(String pathname) {
    File f = new File(pathname);
    File[] listfiles = f.listFiles();
    for (int i = 0; i < listfiles.length; i++) {
        if (listfiles[i].isDirectory()) {
            File[] internalFile = listfiles[i].listFiles();
            for (int j = 0; j < internalFile.length; j++) {
                System.out.println(internalFile[j]);
                if (internalFile[j].isDirectory()) {
                    String name = internalFile[j].getAbsolutePath();
                    listFile(name);
                }

            }
        } else {
            System.out.println(listfiles[i]);
        }

    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...