Як повторити файли в каталозі на 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.