Подтвердить что ты не робот

Список всех файлов из каталога, рекурсивный с Java

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

В Java 8 это 1-лайнер через 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

Ответ 3

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

Он использует класс пути Java 7 NIO.

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

Ответ 4

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

Ответ 5

Быстрый способ получить содержимое каталога с помощью 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();

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

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

String[] types = {"mp3", "flac"};
Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);

Ответ 9

Это будет прекрасно работать

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

Ответ 10

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

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

    }

}

Ответ 11

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

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

Ответ 12

Просто так вы знаете, что isDirectory() - довольно медленный метод. В моем файловом браузере я нахожу его довольно медленным. Я буду искать в библиотеке, чтобы заменить его собственным кодом.

Ответ 13

Более эффективным способом, который я нашел в работе с миллионами папок и файлов, является захват списка каталогов с помощью команды DOS в каком-то файле и ее анализ. После анализа данных вы можете выполнить анализ и вычислить статистику.

Ответ 14

import java.io.*;

public class MultiFolderReading {

public void checkNoOfFiles (String filename) throws IOException {

    File dir=new File(filename);
    File files[]=dir.listFiles();//files array stores the list of files

 for(int i=0;i<files.length;i++)
    {
        if(files[i].isFile()) //check whether files[i] is file or directory
        {
            System.out.println("File::"+files[i].getName());
            System.out.println();

        }
        else if(files[i].isDirectory())
        {
            System.out.println("Directory::"+files[i].getName());
            System.out.println();
            checkNoOfFiles(files[i].getAbsolutePath());
        }
    }
}

public static void main(String[] args) throws IOException {

    MultiFolderReading mf=new MultiFolderReading();
    String str="E:\\file"; 
    mf.checkNoOfFiles(str);
   }
}

Ответ 15

В Guava вам не нужно ждать, пока коллекция будет возвращена вам, но на самом деле может перебирать файлы. Легко представить интерфейс IDoSomethingWithThisFile в сигнатуре следующей функции:

public static void collectFilesInDir(File dir) {
    TreeTraverser<File> traverser = Files.fileTreeTraverser();
    FluentIterable<File> filesInPostOrder = traverser.preOrderTraversal(dir);
    for (File f: filesInPostOrder)
        System.out.printf("File: %s\n", f.getPath());
}

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

Ответ 16

Java 8

public static void main(String[] args) throws IOException {

        Path start = Paths.get("C:\\data\\");
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            List<String> collect = stream
                .map(String::valueOf)
                .sorted()
                .collect(Collectors.toList());

            collect.forEach(System.out::println);
        }


    }

Ответ 17

public class GetFilesRecursive {
    public static List <String> getFilesRecursively(File dir){
        List <String> ls = new ArrayList<String>();
        for (File fObj : dir.listFiles()) {
            if(fObj.isDirectory()) {
                ls.add(String.valueOf(fObj));
                ls.addAll(getFilesRecursively(fObj));               
            } else {
                ls.add(String.valueOf(fObj));       
            }
        }

        return ls;
    }
    public static List <String> getListOfFiles(String fullPathDir) {
        List <String> ls = new ArrayList<String> ();
        File f = new File(fullPathDir);
        if (f.exists()) {
            if(f.isDirectory()) {
                ls.add(String.valueOf(f));
                ls.addAll(getFilesRecursively(f));
            }
        } else {
            ls.add(fullPathDir);
        }
        return ls;
    }

    public static void main(String[] args) {
        List <String> ls = getListOfFiles("/Users/srinivasab/Documents");
        for (String file:ls) {
            System.out.println(file);
        }
        System.out.println(ls.size());
    }
}

Ответ 18

Еще один оптимизированный код

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class GetFilesRecursive {
    public static List <String> getFilesRecursively(File dir){
        List <String> ls = new ArrayList<String>();
        if (dir.isDirectory())
            for (File fObj : dir.listFiles()) {
                if(fObj.isDirectory()) {
                    ls.add(String.valueOf(fObj));
                    ls.addAll(getFilesRecursively(fObj));               
                } else {
                    ls.add(String.valueOf(fObj));       
                }
            }
        else
            ls.add(String.valueOf(dir));

        return ls;
    }

    public static void main(String[] args) {
        List <String> ls = getFilesRecursively(new File("/Users/srinivasab/Documents"));
        for (String file:ls) {
            System.out.println(file);
        }
        System.out.println(ls.size());
    }
}