Як повторити файли в каталозі на Java?


175

Мені потрібно отримати список усіх файлів у каталозі, включаючи файли у всіх підкаталогах. Який стандартний спосіб здійснити ітерацію каталогу в Java?

Відповіді:


207

Ви можете використовувати File#isDirectory()для перевірки, чи даний файл (шлях) є каталогом. Якщо це так true, то ви знову викликаєте той самий метод з його File#listFiles()результатом. Це називається рекурсією .

Ось основний приклад старту.

public static void main(String... args) {
    File[] files = new File("C:/").listFiles();
    showFiles(files);
}

public static void showFiles(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            System.out.println("Directory: " + file.getName());
            showFiles(file.listFiles()); // Calls same method again.
        } else {
            System.out.println("File: " + file.getName());
        }
    }
}

Зауважте, що це чутливо до того, StackOverflowErrorколи дерево знаходиться глибше, ніж може містити стек JVM. Ви можете скористатися ітеративним підходом або рекурсією хвоста , але це інша тема;)


дякую Балю, будь-яка ідея про те, наскільки глибоко це може бути загальною здогадкою?
Джеймс

10
Залежить від налаштувань пам'яті вашого JVM. Але загалом щось на кшталт кількох тисяч. Якщо ви думаєте, що можете коли-небудь натрапити на такий каталог, не використовуйте рекурсії.
Майк Баранчак

4
Це чутливо до того, NullPointerExceptionколи файлова система змінюється між викликом на isDirectoryі listFilesяк це може статися, якщо System.out.printlnблокується або вам просто не пощастить. Перевірка того, що результат listFilesне є нульовим, вирішить цю умову гонки.
Майк Семюель

1
@BoratSagdiyev, Не використовуючи API старих файлів Java, але якщо ви користуєтесь сучасним JVM, тоді java.nio.file.DirectoryStreamви можете переглядати каталог, і він може бути реалізований, щоб мати невеликий слід пам'яті, але єдиним способом сказати точно було б контролювати використання пам'яті на певній платформі.
Майк Самуель

1
Папка "C: \\" - не найкращий вибір прикладу)
В'ячеслав

86

Якщо ви використовуєте Java 1.7, ви можете використовувати java.nio.file.Files.walkFileTree(...) .

Наприклад:

public class WalkFileTreeExample {

  public static void main(String[] args) {
    Path p = Paths.get("/usr");
    FileVisitor<Path> fv = new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        System.out.println(file);
        return FileVisitResult.CONTINUE;
      }
    };

    try {
      Files.walkFileTree(p, fv);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

Якщо ви використовуєте Java 8, ви можете використовувати інтерфейс потоку за допомогою java.nio.file.Files.walk(...):

public class WalkFileTreeExample {

  public static void main(String[] args) {
    try (Stream<Path> paths = Files.walk(Paths.get("/usr"))) {
      paths.forEach(System.out::println);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

1
чи є спосіб за допомогою потоків поставити контрольну точку, коли проходить новий каталог і виконати функцію?
Raghu DV

28

Ознайомтеся з класом FileUtils в Apache Commons - конкретно ітераційними файлами :

Дозволяє ітерацію файлів у вказаному каталозі (та необов'язково його підкаталогах).


5
Цей API не є справді потоковим (якщо ви піклуєтесь про використання пам’яті), він спочатку генерує колекцію, лише потім повертає через неї ітератор: return listFiles (каталог, fileFilter, dirFilter) .iterator ();
Гілі Нахум

Хороший варіант для Java 1.6.
Давид І.

Погодьтеся з @GiliNachum. FileUtils від Apache спочатку збирає всі файли та дає їм ітератор. Це небезпечно для ресурсів, якщо у вас величезна кількість файлів.
Богдан Самондрос

8

Для Java 7+ також є https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Приклад, взятий з Javadoc:

List<Path> listSourceFiles(Path dir) throws IOException {
   List<Path> result = new ArrayList<>();
   try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{c,h,cpp,hpp,java}")) {
       for (Path entry: stream) {
           result.add(entry);
       }
   } catch (DirectoryIteratorException ex) {
       // I/O error encounted during the iteration, the cause is an IOException
       throw ex.getCause();
   }
   return result;
}

8

Використання org.apache.commons.io.FileUtils

File file = new File("F:/Lines");       
Collection<File> files = FileUtils.listFiles(file, null, true);     
for(File file2 : files){
    System.out.println(file2.getName());            
} 

Використовуйте false, якщо ви не хочете, щоб файли з підкаталогів.


3

Це дерево, тому рекурсія - ваш друг: почніть з батьківського каталогу та зателефонуйте методу, щоб отримати масив дочірніх файлів. Ітерація через дочірній масив. Якщо поточне значення - це каталог, переведіть його на рекурсивний виклик вашого методу. Якщо ні, обробіть файл листа належним чином.


2

Як зазначалося, це проблема рекурсії. Зокрема, ви можете поглянути

listFiles() 

У Java-файлі API тут . Він повертає масив усіх файлів у каталозі. Використовуючи це разом з

isDirectory()

побачити, чи потрібно повторювати далі - це хороший початок.


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

0

Щоб додати відповідь @msandiford, оскільки більшість випадків, коли дерево файлів ходить u, можливо, захочеться виконати функцію у вигляді каталогу або будь-якого конкретного файлу. Якщо ви неохоче використовуєте потоки. Можуть бути реалізовані наступні способи, які перекрито

Files.walkFileTree(Paths.get(Krawl.INDEXPATH), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
    new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException {
                // Do someting before directory visit
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException {
                // Do something when a file is visited
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException {
                // Do Something after directory visit 
                return FileVisitResult.CONTINUE;
        }
});

0

Ви також можете неправильно використовувати File.list (FilenameFilter) (та варіанти) для обходу файлів. Короткий код і працює у ранніх версіях Java, наприклад:

// list files in dir
new File(dir).list(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        String file = dir.getAbsolutePath() + File.separator + name;
        System.out.println(file);
        return false;
    }
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.