Як мені рекурсивно перераховувати всі файли в каталозі на Java? Чи надає рамка якусь корисність?
Я бачив багато реактивних реалізацій. Але ніхто з фреймворку чи ніо
Як мені рекурсивно перераховувати всі файли в каталозі на Java? Чи надає рамка якусь корисність?
Я бачив багато реактивних реалізацій. Але ніхто з фреймворку чи ніо
Відповіді:
Java 8 забезпечує хороший потік для обробки всіх файлів у дереві.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Це забезпечує природний спосіб переміщення файлів. Оскільки це потік, ви можете виконувати всі хороші операції потоку за результатами, такими як обмеження, групування, картографування, вихід рано та ін.
ОНОВЛЕННЯ : Я можу зазначити, що також існує Files.find, який має BiPredicate, який може бути більш ефективним, якщо вам потрібно перевірити атрибути файлів.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
Зауважте, що хоча JavaDoc ухиляється від того, що цей метод може бути більш ефективним, ніж Files.walk, він фактично ідентичний, різницю в продуктивності можна спостерігати, якщо ви також отримуєте атрибути файлів у своєму фільтрі. Зрештою, якщо вам потрібно фільтрувати за атрибутами, використовуйте Files.find , інакше використовуйте Files.walk , в основному тому, що є перевантаження і це зручніше.
ТЕСТИ : Як я вимагав, я дав порівняння ефективності багатьох відповідей. Перегляньте проект Github, який містить результати та тестовий випадок .
Files.walk
найкраще використовувати паралельний потік, за Files.walkFileTree
яким слід стежити лише за незначним сповільненням. Прийнята відповідь, що використовує commons-io, є моїми тестами найбільш повільними, а в 4 рази повільнішими.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Як я міг це виправити
У FileUtils є iterateFiles
і listFiles
методи. Спробуйте. (від commons-io )
Редагувати: Ви можете перевірити тут тест на різні підходи. Здається , що Загально-IO підхід повільно, тому вибрати деякі з них швидше , звідси (якщо це важливо)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, де dir
є об'єкт File, який вказує на базовий каталог.
listFilesAndDirs()
, оскільки listFiles()
не повертає порожні папки.
FileUtils.listFiles(dir, true, true)
. використання FileUtils.listFiles(dir, null, true)
буде кидати виняток, тоді як FileUtils.listFiles(dir, true, null)
список усіх файлів, не заглядаючи в підкаталоги.
// Готовий до запуску
import java.io.File;
public class Filewalker {
public void walk( String path ) {
File root = new File( path );
File[] list = root.listFiles();
if (list == null) return;
for ( File f : list ) {
if ( f.isDirectory() ) {
walk( f.getAbsolutePath() );
System.out.println( "Dir:" + f.getAbsoluteFile() );
}
else {
System.out.println( "File:" + f.getAbsoluteFile() );
}
}
}
public static void main(String[] args) {
Filewalker fw = new Filewalker();
fw.walk("c:\\" );
}
}
-> .
.
"/"
, "./"
або "../"
для кореневого каталогу, поточний робочий каталог і батьківський каталог відповідно
Java 7 матиме має Files.walkFileTree :
Якщо ви надаєте початкову точку і відвідувачу файлу, він буде викликати різні методи для відвідувача файлу, коли він проходить через файл у дереві файлів. Ми очікуємо, що люди будуть використовувати це, якщо вони розробляють рекурсивну копію, рекурсивний хід, рекурсивне видалення або рекурсивну операцію, яка встановлює дозволи або виконує іншу операцію над кожним із файлів.
Зараз є цілий підручник Oracle з цього питання .
Зовнішні бібліотеки не потрібні.
Повертає колекцію, щоб ви могли робити з нею все, що завгодно, після дзвінка.
public static Collection<File> listFileTree(File dir) {
Set<File> fileTree = new HashSet<File>();
if(dir==null||dir.listFiles()==null){
return fileTree;
}
for (File entry : dir.listFiles()) {
if (entry.isFile()) fileTree.add(entry);
else fileTree.addAll(listFileTree(entry));
}
return fileTree;
}
Я б пішов з чимось на кшталт:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
System.out.println є просто там, щоб вказати, що потрібно зробити з файлом. немає необхідності розмежовувати файли та каталоги, оскільки у звичайному файлі просто буде нульова дитина.
listFiles()
: "Якщо це абстрактне ім'я шляху не позначає каталог, тоді цей метод повертається null
."
Я вважаю за краще використовувати чергу над рекурсією для такого простого переходу:
List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
for (File f : dirs.poll().listFiles()) {
if (f.isDirectory()) {
dirs.add(f);
} else if (f.isFile()) {
allFiles.add(f);
}
}
}
просто напишіть його самостійно, використовуючи просту рекурсію:
public List<File> addFiles(List<File> files, File dir)
{
if (files == null)
files = new LinkedList<File>();
if (!dir.isDirectory())
{
files.add(dir);
return files;
}
for (File file : dir.listFiles())
addFiles(files, file);
return files;
}
Я думаю, що це має зробити цю роботу:
File dir = new File(dirname);
String[] files = dir.list();
Таким чином у вас є файли та режими. Тепер використовуйте рекурсію і робіть те ж саме для dirs ( File
клас має isDirectory()
метод).
З Java 7 ви можете використовувати наступний клас:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class MyFileIterator extends SimpleFileVisitor<Path>
{
public MyFileIterator(String path) throws Exception
{
Files.walkFileTree(Paths.get(path), this);
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) throws IOException
{
System.out.println("File: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attributes) throws IOException
{
System.out.println("Dir: " + dir);
return FileVisitResult.CONTINUE;
}
}
Цей код готовий до запуску
public static void main(String... args) {
File[] files = new File("D:/").listFiles();
if (files != null)
getFiles(files);
}
public static void getFiles(File[] files) {
for (File file : files) {
if (file.isDirectory()) {
getFiles(file.listFiles());
} else {
System.out.println("File: " + file);
}
}
}
Окрім рекурсивного обходу, можна також використовувати підхід для відвідувачів.
Нижче коду використовується підхід для відвідування на основі відвідувачів. Очікується, що вхід до програми - це кореневий каталог для проходження.
public interface Visitor {
void visit(DirElement d);
void visit(FileElement f);
}
public abstract class Element {
protected File rootPath;
abstract void accept(Visitor v);
@Override
public String toString() {
return rootPath.getAbsolutePath();
}
}
public class FileElement extends Element {
FileElement(final String path) {
rootPath = new File(path);
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
}
public class DirElement extends Element implements Iterable<Element> {
private final List<Element> elemList;
DirElement(final String path) {
elemList = new ArrayList<Element>();
rootPath = new File(path);
for (File f : rootPath.listFiles()) {
if (f.isDirectory()) {
elemList.add(new DirElement(f.getAbsolutePath()));
} else if (f.isFile()) {
elemList.add(new FileElement(f.getAbsolutePath()));
}
}
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
public Iterator<Element> iterator() {
return elemList.iterator();
}
}
public class ElementWalker {
private final String rootDir;
ElementWalker(final String dir) {
rootDir = dir;
}
private void traverse() {
Element d = new DirElement(rootDir);
d.accept(new Walker());
}
public static void main(final String[] args) {
ElementWalker t = new ElementWalker("C:\\temp");
t.traverse();
}
private class Walker implements Visitor {
public void visit(final DirElement d) {
System.out.println(d);
for(Element e:d) {
e.accept(this);
}
}
public void visit(final FileElement f) {
System.out.println(f);
}
}
}
Ви можете використовувати код нижче, щоб отримати список файлів певної папки чи каталогу рекурсивно.
public static void main(String args[]) {
recusiveList("D:");
}
public static void recursiveList(String path) {
File f = new File(path);
File[] fl = f.listFiles();
for (int i = 0; i < fl.length; i++) {
if (fl[i].isDirectory() && !fl[i].isHidden()) {
System.out.println(fl[i].getAbsolutePath());
recusiveList(fl[i].getAbsolutePath());
} else {
System.out.println(fl[i].getName());
}
}
}
Загальноприйнятий відповідь великий, проте він ламається , коли ви хочете зробити IO всередині лямбда.
Ось що ви можете зробити, якщо ваша дія оголошує IOExceptions.
Ви можете ставитися до відфільтрованого потоку як до Iterable
, а потім виконувати свою дію у звичайній для кожного циклу. Таким чином, вам не доведеться обробляти винятки всередині лямбда.
try (Stream<Path> pathStream = Files.walk(Paths.get(path))
.filter(Files::isRegularFile)) {
for (Path file : (Iterable<Path>) pathStream::iterator) {
// something that throws IOException
Files.copy(file, System.out);
}
}
Знайшов цю хитрість тут: https://stackoverflow.com/a/32668807/1207791
Нерекурсивний BFS з єдиним списком (конкретний приклад - пошук файлів * .eml):
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".eml");
}
};
// BFS recursive search
List<File> queue = new LinkedList<File>();
queue.addAll(Arrays.asList(dir.listFiles(filter)));
for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
File file = itr.next();
if (file.isDirectory()) {
itr.remove();
for (File f: file.listFiles(filter)) itr.add(f);
}
}
Моя версія (звичайно, я міг би використовувати вбудовану прогулянку на Java 8 ;-)):
public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
ArrayList<File> collected = new ArrayList<>();
walk(rootDir, predicate, collected);
return collected;
}
private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
Stream.of(listOnlyWhenDirectory(dir))
.forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
}
private static File[] listOnlyWhenDirectory(File dir) {
return dir.isDirectory() ? dir.listFiles() : new File[]{};
}
private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
if (filterFunction.test(toAdd)) {
files.add(toAdd);
}
return files;
}
Ось просте, але ідеально працююче рішення з використанням recursion
:
public static List<Path> listFiles(String rootDirectory)
{
List<Path> files = new ArrayList<>();
listFiles(rootDirectory, files);
return files;
}
private static void listFiles(String path, List<Path> collectedFiles)
{
File root = new File(path);
File[] files = root.listFiles();
if (files == null)
{
return;
}
for (File file : files)
{
if (file.isDirectory())
{
listFiles(file.getAbsolutePath(), collectedFiles);
} else
{
collectedFiles.add(file.toPath());
}
}
}
private void fillFilesRecursively(File file, List<File> resultFiles) {
if (file.isFile()) {
resultFiles.add(file);
} else {
for (File child : file.listFiles()) {
fillFilesRecursively(child, resultFiles);
}
}
}
Я придумав це для рекурсивного друку всіх файлів / імен файлів.
private static void printAllFiles(String filePath,File folder) {
if(filePath==null) {
return;
}
File[] files = folder.listFiles();
for(File element : files) {
if(element.isDirectory()) {
printAllFiles(filePath,element);
} else {
System.out.println(" FileName "+ element.getName());
}
}
}
Приклад виводить * .csv файли в рекурсивному пошуку підкаталогів за допомогою Files.find () з java.nio:
String path = "C:/Daten/ibiss/ferret/";
logger.debug("Path:" + path);
try (Stream<Path> fileList = Files.find(Paths.get(path), Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith("csv"))) {
List<String> someThingNew = fileList.sorted().map(String::valueOf).collect(Collectors.toList());
for (String t : someThingNew) {
t.toString();
logger.debug("Filename:" + t);
}
}
Розміщуючи цей приклад, оскільки у мене виникли проблеми з розумінням того, як передавати параметр імені файлу в прикладі №1, наведеному Брайаном, використовуючи функцію foreach на потоковий результат -
Сподіваюсь, це допомагає.
Котлін має FileTreeWalk
для цього. Наприклад:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Створить текстовий список усіх файлів без каталогу під заданим коренем, по одному файлу на рядок із контуром відносно кореня та довжини.
На основі відповіді штабеля. Ось рішення, що працює в JSP без будь-яких зовнішніх бібліотек, так що ви можете розмістити його практично будь-де на своєму сервері:
<!DOCTYPE html>
<%@ page session="false" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%!
public List<String> files = new ArrayList<String>();
/**
Fills files array with all sub-files.
*/
public void walk( File root ) {
File[] list = root.listFiles();
if (list == null) return;
for ( File f : list ) {
if ( f.isDirectory() ) {
walk( f );
}
else {
files.add(f.getAbsolutePath());
}
}
}
%>
<%
files.clear();
File jsp = new File(request.getRealPath(request.getServletPath()));
File dir = jsp.getParentFile();
walk(dir);
String prefixPath = dir.getAbsolutePath() + "/";
%>
Тоді ви просто робите щось на кшталт:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>