我需要获取一个目录中所有文件的列表,包括所有子目录中的文件。如何使用Java实现目录迭代的标准方式?
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());
}
}
}
}
StackOverflowError
。如果您已经使用Java 8或更新版本,则最好使用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.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.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();
}
}
}
Files.walk(somePath)
将遍历从根目录可访问的整个文件和目录树。如果您只需要处理特定的文件,例如按其扩展名分类,则可以使用 filter
和 PathMatcher
,将其配置为 glob:**.xml
,即在后续管道阶段中省略任何非 XML 文件。 - Roman Vottner请查看Apache Commons中的FileUtils类,特别是 iterateFiles :
允许对给定目录(以及可选的子目录)中的文件进行迭代。
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;
}
这是一棵树形结构,所以递归是您的好帮手:从父目录开始调用方法来获取子文件数组。遍历子数组。如果当前值是一个目录,将其传递给方法的递归调用。如果不是,则适当地处理叶子文件。
您也可以误用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;
}
});
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;
}
});
我喜欢使用Optional和streams来获得一个简洁明了的解决方案,我使用以下代码来迭代一个目录。以下情况由代码处理:
但正如其他人所提到的,如果你有大量文件夹,仍然需要注意内存溢出的问题。
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>
isDirectory
和listFiles
之间发生更改,就可能导致出现NullPointerException
错误,例如如果System.out.println
阻塞或运气不佳。检查listFiles
的输出结果是否为 null 可以解决这个竞态条件。 - Mike Samueljava.nio.file.DirectoryStream
迭代一个目录,可以实现小内存占用,但确定内存占用情况的唯一方法是在特定平台上监视内存使用情况。 - Mike SamuelDirectoryStream
的注释,请参考 Wim Deblauwe 的答案,因为它列出了重要的方面。应该关闭流以防止资源泄漏。 - Svilen