如何在Java中遍历目录及其子目录中的文件?

我需要获得一个目录中所有文件的列表,包括所有子目录中的文件。用Java完成目录迭代的标准方法是什么?

218698 次浏览

它是一棵树,所以递归是你的朋友:从父目录开始,调用方法来获得子文件数组。循环访问子数组。如果当前值是一个目录,则将其传递给方法的递归调用。如果不是,请适当处理叶文件。

查看Apache Commons中的FileUtils类-特别是迭代文件

允许迭代给定目录(及其可选的子目录)中的文件。

如前所述,这是一个递归问题。特别是,您可能需要查看

listFiles()

在Java文件API在这里中。它返回目录中所有文件的数组。将此与一起使用

isDirectory()

看看是否需要进一步递归是一个好的开始。

您可以使用File#isDirectory()来测试给定的文件(路径)是否为目录。如果这是true,那么您只需使用其File#listFiles()结果再次调用相同的方法。这称为递归

下面是一个基本的启动示例:

package com.stackoverflow.q3154488;


import java.io.File;


public class Demo {


public static void main(String... args) {
File dir = new File("/path/to/dir");
showFiles(dir.listFiles());
}


public static void showFiles(File[] files) {
for (File file : files) {
if (file.isDirectory()) {
System.out.println("Directory: " + file.getAbsolutePath());
showFiles(file.listFiles()); // Calls same method again.
} else {
System.out.println("File: " + file.getAbsolutePath());
}
}
}
}

请注意,当树的深度超过JVM的堆栈所能容纳的深度时,这对StackOverflowError很敏感。如果您已经在使用Java8或更高版本,那么您最好使用Files#walk(),它利用尾递归

package com.stackoverflow.q3154488;


import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;


public class DemoWithJava8 {


public static void main(String... args) throws Exception {
Path dir = Paths.get("/path/to/dir");
Files.walk(dir).forEach(path -> showFile(path.toFile()));
}


public static void showFile(File file) {
if (file.isDirectory()) {
System.out.println("Directory: " + file.getAbsolutePath());
} else {
System.out.println("File: " + file.getAbsolutePath());
}
}
}

如果您使用的是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();
}
}


}

使用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.

对于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;
}

补充一下@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;
}
});

您还可以误用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;
}
});

我喜欢使用可选溪流来获得一个简单而清晰的解决方案。 我使用下面的代码来遍历一个目录。以下情况由《守则》处理:

  1. 处理空目录的情况
  2. 懒惰

但正如其他人所提到的,如果你有巨大的文件夹,你仍然需要注意内存不足。

    File directoryFile = new File("put your path here");
Stream<File> files = Optional.ofNullable(directoryFile// directoryFile
.listFiles(File::isDirectory)) // filter only directories(change with null if you don't need to filter)
.stream()
.flatMap(Arrays::stream);// flatmap from Stream<File[]> to Stream<File>