Как мне рекурсивно перечислить все файлы в каталоге в 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 медленный, поэтому выберите некоторые из более быстрых отсюда (если это имеет значение)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
Где», где dir
находится объект «Файл», указывающий на базовый каталог.
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);
}
}
}
Помимо рекурсивного обхода можно использовать подход на основе Visitor.
В приведенном ниже коде используется подход, основанный на посетителях, для обхода. Ожидается, что вход в программу является корневым каталогом для прохождения.
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
, а затем выполнять свои действия в обычном цикле for-each. Таким образом, вам не нужно обрабатывать исключения внутри лямбды.
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 в Stream-result -
Надеюсь это поможет.
Котлин 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>