Как перебрать файлы в каталоге на 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. Вы можете вместо этого использовать итеративный подход или хвостовую рекурсию , но это уже другая тема;)

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

}
clstrfsck
источник
1
Есть ли способ с потоками поставить контрольную точку при прохождении нового каталога и выполнить функцию?
Рагху Д.В.
28

Проверьте класс FileUtils в Apache Commons - в частности, iterateFiles :

Позволяет перебирать файлы в данном каталоге (и, необязательно, его подкаталоги).

Бен Дж
источник
5
Этот API не является потоковым (если вы заботитесь об использовании mem), он сначала генерирует коллекцию, а затем возвращает итератор над ней: return listFiles (directory, 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, если вы не хотите файлы из подкаталогов.

fjkjava
источник
3

Это дерево, поэтому рекурсия - ваш друг: начните с родительского каталога и вызовите метод, чтобы получить массив дочерних файлов. Итерация по дочернему массиву. Если текущее значение является каталогом, передайте его рекурсивному вызову вашего метода. Если нет, обработайте файл листа соответствующим образом.

duffymo
источник
2

Как уже отмечалось, это проблема рекурсии. В частности, вы можете посмотреть на

listFiles() 

В Java File API здесь . Возвращает массив всех файлов в каталоге. Используя это вместе с

isDirectory()

посмотреть, нужно ли вам продолжать курс, - это хорошее начало.

Chimmy
источник
Эта ссылка может быть полезна, так как ссылка в ответе не работает.
Donglecow
0

Чтобы добавить ответ @msandiford, так как в большинстве случаев при обходе дерева файлов вам может потребоваться выполнить функцию как каталог или посещение любого конкретного файла. Если вы не хотите использовать потоки. Следующие методы могут быть переопределены

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;
    }
});
Роб Клинхамер
источник