Как мне рекурсивно перечислить все файлы в каталоге в Java? Предоставляет ли фреймворк какую-либо полезность?
Я видел много хакерских реализаций. Но ни один из фреймворков или nio а>
Как мне рекурсивно перечислить все файлы в каталоге в Java? Предоставляет ли фреймворк какую-либо полезность?
Я видел много хакерских реализаций. Но ни один из фреймворков или nio а>
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 раза медленнее.
- person Brett Ryan; 28.06.2015
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Как я мог это исправить
- person Kachna; 05.09.2015
forEach
используйте collect
с набором или сборщиком списков, например .collect(Collectors.toList())
. Если вы можете избежать использования коллектора в своей логике, тем лучше, поскольку вы будете использовать меньше объектов и обработка будет быстрее.
- person Brett Ryan; 10.10.2015
Files.walk(Paths.get(path), FileVisitOption.FOLLOW_LINKS)
- person Matt Passell; 03.03.2016
Path basePath = Files.isSymbolicLink(basePath) ? Files.readSymbolicLink(basePath) : basePath;
, а затем пройти по дереву, не переходя по ссылкам. :)
- person Matt Passell; 11.03.2016
.collect(stagingFileList::add)
в результирующем потоке из этого кода дает мне Collector<Path,A,R> is not a functional interface
в параметре сбора. Этот стрим - это круто, но я не совсем уверен, что я ищу, чтобы это исправить.
- person Amalgovinus; 13.01.2017
Files.find( Paths.get(path), Integer.MAX_VALUE, (filePath, fileAttr) -> fileAttr.isRegularFile()) .collect(list::add);
- person Amalgovinus; 13.01.2017
Collectors.toList()
- person Brett Ryan; 13.01.2017
FileUtils иметь _ 1_ и _ 2_. Дайте им попробовать. (из commons-io)
Изменить: вы можете проверить здесь тест различных подходов. Кажется, что подход commons-io медленный, поэтому выберите несколько более быстрых отсюда (если это важно)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, где dir
- это объект File, указывающий на базовый каталог.
- person andronikus; 27.04.2012
listFilesAndDirs()
, поскольку listFiles()
не возвращает пустые папки.
- person schnatterer; 14.02.2014
FileUtils.listFiles(dir, true, true)
. использование FileUtils.listFiles(dir, null, true)
вызовет исключение, а FileUtils.listFiles(dir, true, null)
выведет список всех файлов без просмотра подкаталогов.
- person ocramot; 23.05.2014
// Готов к запуску
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:\\" );
}
}
-> .
.
- person Brett Ryan; 28.06.2015
"../"
для корневого каталога, текущего рабочего каталога и родительского каталога соответственно
- person Moses Kirathe; 14.06.2020
В 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
».
- person hfs; 17.11.2011
Я предпочитаю использовать очередь рекурсии для такого простого обхода:
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;
}
В 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;
}
}
Я думаю, это должно работать:
File dir = new File(dirname);
String[] files = dir.list();
Таким образом, у вас есть файлы и каталоги. Теперь используйте рекурсию и сделайте то же самое для каталогов (класс File
имеет метод isDirectory()
).
В Java 8 теперь мы можем использовать утилиту Files для просмотра дерева файлов. Очень простой.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Этот код готов к запуску
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());
}
}
}
Перечисляет все файлы с предоставленными расширениями, с возможностью сканирования вложенных папок (рекурсивно)
public static ArrayList<File> listFileTree(File dir,boolean recursive) {
if (null == dir || !dir.isDirectory()) {
return new ArrayList<>();
}
final Set<File> fileTree = new HashSet<File>();
FileFilter fileFilter = new FileFilter() {
private final String[] acceptedExtensions = new String[]{"jpg", "png", "webp", "jpeg"};
@Override
public boolean accept(File file) {
if (file.isDirectory()) {
return true;
}
for (String extension : acceptedExtensions) {
if (file.getName().toLowerCase().endsWith(extension)) {
return true;
}
}
return false;
}
};
File[] listed = dir.listFiles(fileFilter);
if(listed!=null){
for (File entry : listed) {
if (entry.isFile()) {
fileTree.add(entry);
} else if(recursive){
fileTree.addAll(listFileTree(entry,true));
}
}
}
return new ArrayList<>(fileTree);
}
Нерекурсивная 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 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());
}
}
}
В Kotlin есть FileTreeWalk
для этого. Например:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Создает текстовый список всех файлов, не являющихся каталогами, в заданном корне, по одному файлу в строке с путем относительно корня и длиной.
принятый ответ великолепен, однако он не работает, когда вы хотите выполнить ввод-вывод внутри лямбда.
Вот что вы можете сделать, если ваше действие объявляет исключения 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
По-другому можно поступить, даже если кто-то уже предоставил прогулку по Java 8.
Это рекурсивно предоставит вам все файлы
private Stream<File> files(File file) {
return file.isDirectory()
? Arrays.stream(file.listFiles()).flatMap(this::files)
: Stream.of(file);
}
public static String getExten(String path) {
int i = path.lastIndexOf('.');
if (i > 0) {
return path.substring(i);
}
else return "";
}
public static List<String> GetAllFiles(String path, List<String>fileList){
File file = new File(path);
File[] files = file.listFiles();
for(File folder:files) {
if(extensions.contains(getExten(folder.getPath()))) {
fileList.add(folder.getPath());
}
}
File[] direcs = file.listFiles(File::isDirectory);
for(File dir:direcs) {
GetAllFiles(dir.getPath(),fileList);
}
return fileList;
}
Это простая рекурсивная функция, которая должна предоставить вам все файлы. extension - это список строк, который содержит только те расширения, которые принимаются. Примеры расширений = [.txt, .docx] и т. Д.
Пример выводит файлы * .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 -
Надеюсь это поможет.
на основе ответа @Michael добавьте проверку, возвращает ли listFiles значение null
static Stream<File> files(File file) {
return file.isDirectory()
? Optional.ofNullable(file.listFiles()).map(Stream::of).orElseGet(Stream::empty).flatMap(MainActivity::files)
: Stream.of(file);
}
или используйте Lightweight-Stream-API, который поддерживает Android5 и Android6.
static Stream<File> files(File f) {
return f.isDirectory() ? Stream.ofNullable(f.listFiles()).flatMap(MainActivity::files) : Stream.of(f);
}
На основании ответа укладчика. Вот решение, работающее в 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>