Java遍历文件夹
Java遍历文件夹
简单写了一个打印目录下所有文件以及文件夹的代码,类比于树的遍历,写了深度优先和广度优先的遍历。并且还写了个JDK1.7接口提供的的版本。
深度广度遍历都用到的双端队列(ArrayDeque),不了解双端队列的话可以去学习一下。我简单说一下,双端队列既可以用作队列,也可以用作栈,而且效率比较高。
1.1 深度优先遍历
使用栈存储File对象,出栈后,输出file信息,若是文件夹,就将文件夹下的子文件以及子文件夹全部压入栈,栈空则遍历完成。
代码
//深度优先遍历 Depth First Search
//使用栈
static void dfs(File file) {
if (file == null || !file.exists()) {
return;
}
final Deque<File> stack = new ArrayDeque<>();
stack.push(file);
// stack.pop();
// stack.pollFirst();
while ((file = stack.pollFirst()) != null) {
System.out.println(file);
/*
if (!file.exists()) {
//文件存在,但系统拒绝访问
}
*/
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
System.err.println(file + "\t文件夹访问受限,跳过");
continue;
}
for (int filesLength = files.length, i = filesLength - 1; i >= 0; i--) {
stack.push(files[i]);
}
}
}
}
此处注意:
- 使用
file.listFiles()
得到的子文件以及文件夹不一定能访问。若调用file.exists()
返回为false则为无法访问。 - 若
file
为文件夹,则file.listFiles()
应返回File数组。此处做判断是为了程序的鲁棒性。
1.2 广度优先遍历
使用队列储存File对象,利用队列先入先出的特性,遍历文件夹。大学时,我就用C语言写了一个层次遍历二叉树的实现,只是当时遍历出的结果没有分层。这次在此基础上,使用list记录了每一层的文件及文件夹个数,并输出。
代码
//广度优先遍历 Breath First Search
//使用队列
static List<Integer> bfs(File file) {
if (file == null || !file.exists()) {
return Collections.emptyList();
}
final Queue<File> queue = new ArrayDeque<>();
queue.offer(file);
//每一层包含元素的个数
final List<Integer> list = new ArrayList<>();
//i 循环次数
//j 层数
//n 该层包含元素个数
int i = 0, j = 0, n = 0;
list.add(1);
while ((file = queue.poll()) != null) {
System.out.println(file);
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
System.err.println(file + "\t文件夹访问受限,跳过");
continue;
}
n += files.length;
Arrays.stream(files).forEach(queue::offer);
}
i++;
if (i == list.get(j)) {
//遍历一层结束
list.add(n);
i = 0;
j++;
n = 0;
}
}
1.3 Java提供 Files.walkFileTree()
这个就没什么好说的了,属于深度优先遍历。大量文件及文件夹时,使用此方法遍历效果十分好。
代码
//nio
static void nio(Path path) throws IOException {
Files.walkFileTree(path, new FileVisitor<Path>() {
//访问文件夹之前调用此方法
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println(dir);
// System.out.println(attrs);
return FileVisitResult.CONTINUE;
}
//访问文件时调用此方法
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
// System.out.println(attrs);
return FileVisitResult.CONTINUE;
}
//访问文件失败时调用此方法
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.err.print(file + "\t文件访问受限: ");
if (exc == null) {
System.err.println("未知错误");
} else {
System.err.println(exc);
// throw exc;
}
return FileVisitResult.CONTINUE;
}
//访问文件夹之后调用此方法
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// System.out.println(dir);
if (exc != null) {
System.err.println(dir + "\t文件夹访问受限,跳过" + exc);
// throw exc;
}
return FileVisitResult.CONTINUE;
}
});
}
删除文件夹的想法
然后我又想着,既然遍历都做完了,要不再试试删除。三个方法。
- 使用Java提供 Files.walkFileTree()。这个是真的便利,只需要在访问文件时和文件夹后调用删除方法即可。
- 使用递归,这个也是相当简单粗暴的方法了。遍历文件夹就可以用递归,代码也时相当简单就不做了。
- 和深度优先遍历相似,删除文件之后,再删除文件夹。
2. 删除文件夹
深度优先遍历,先上代码:
static void delete(File file) {
if (file == null || !file.exists()) {
return;
}
Deque<File> stack = new ArrayDeque<>();
stack.push(file);
while (!stack.isEmpty()) {
file = stack.peek();
if (!file.exists()) {
System.out.println(file + " 文件无法访问");
// stack.poll();
// continue;
}
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null && files.length != 0) {
//为了代码的简洁,选择全部入栈,不做文件、文件夹的判断
for (int filesLength = files.length, i = filesLength - 1; i >= 0; i--) {
stack.push(files[i]);
}
continue;
}
}
//file是文件,或空文件夹时,直接删除
if (file.delete())
System.out.println(file + " 删除成功");
else {
System.out.println(file + " 删除失败");
return;//重点标记
}
stack.poll();
}
}
去掉代码中重点标记的哪一行return
,实际运行此代码会发现一个问题,若有一个文件无法成功删除,便会陷入出栈之后再入栈的死循环当中。
即便时在重点标记上加上一个“删除失败后,出栈File对象,到下个要删除的文件夹”的逻辑后,也不能排除两个文件中文件都无法删除而再度陷入循环的困境中。
此时就需要改变入栈的元素,记录下子文件及文件夹的数量,来判断出栈了。至于这个坑,就不一行会埋上了。
最后附上全部代码(包含import)
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
public class FolderTraversal {
// private static final Deque<File> deque = new ArrayDeque<>();
//深度优先遍历 Depth First Search
//使用栈
static void dfs(File file) {
if (file == null || !file.exists()) {
return;
}
final Deque<File> stack = new ArrayDeque<>();
stack.push(file);
// stack.pop();
// stack.pollFirst();
while ((file = stack.pollFirst()) != null) {
System.out.println(file);
/*
if (!file.exists()) {
//文件存在,但系统拒绝访问
}
*/
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
System.err.println(file + "\t文件夹访问受限,跳过");
continue;
}
for (int filesLength = files.length, i = filesLength - 1; i >= 0; i--) {
stack.push(files[i]);
}
}
}
}
static void delete(File file) {
if (file == null || !file.exists()) {
return;
}
Deque<File> stack = new ArrayDeque<>();
stack.push(file);
while (!stack.isEmpty()) {
file = stack.peek();
if (!file.exists()) {
System.out.println(file + " 文件无法访问");
// stack.poll();
// continue;
}
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null && files.length != 0) {
//为了代码的简洁,选择全部入栈,不做文件、文件夹的判断
for (int filesLength = files.length, i = filesLength - 1; i >= 0; i--) {
stack.push(files[i]);
}
continue;
}
}
//file是文件,或空文件夹时,直接删除
if (file.delete())
System.out.println(file + " 删除成功");
else {
System.out.println(file + " 删除失败");
return;
}
stack.poll();
}
}
//广度优先遍历 Breath First Search
//使用队列
static List<Integer> bfs(File file) {
if (file == null || !file.exists()) {
return Collections.emptyList();
}
final Queue<File> queue = new ArrayDeque<>();
queue.offer(file);
//每一层包含元素的个数
final List<Integer> list = new ArrayList<>();
//i 循环次数
//j 层数
//n 该层包含元素个数
int i = 0, j = 0, n = 0;
list.add(1);
while ((file = queue.poll()) != null) {
System.out.println(file);
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
System.err.println(file + "\t文件夹访问受限,跳过");
continue;
}
n += files.length;
Arrays.stream(files).forEach(queue::offer);
}
i++;
if (i == list.get(j)) {
//遍历一层结束
list.add(n);
i = 0;
j++;
n = 0;
}
}
/*
queue.add(file);
queue.remove();
*/
System.out.println(list);
return list;
}
//nio
static void nio(Path path) throws IOException {
Files.walkFileTree(path, new FileVisitor<Path>() {
//访问文件夹之前调用此方法
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println(dir);
// System.out.println(attrs);
return FileVisitResult.CONTINUE;
}
//访问文件时调用此方法
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file);
// System.out.println(attrs);
return FileVisitResult.CONTINUE;
}
//访问文件失败时调用此方法
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.err.print(file + "\t文件访问受限: ");
if (exc == null) {
System.err.println("未知错误");
} else {
System.err.println(exc);
// throw exc;
}
return FileVisitResult.CONTINUE;
}
//访问文件夹之后调用此方法
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// System.out.println(dir);
if (exc != null) {
System.err.println(dir + "\t文件夹访问受限,跳过" + exc);
// throw exc;
}
return FileVisitResult.CONTINUE;
}
});
}
public static void main(String[] args) throws IOException {
// long start = System.currentTimeMillis();
//java中,链接是一个文件 xxx.lnk
// isFile() 判断为true
// isDirectory() 判断为false
File file = new File("files");
// dfs(file);//103322 59316
// bfs(file);//98156 67997
// Path path = Paths.get("");
// nio(path);//30058 17460
// long end = System.currentTimeMillis();
// System.out.println(end - start);
}
}
本文来自博客园,作者:康舒服冰红茶,转载请注明原文链接:https://www.cnblogs.com/pong137/p/16749687.html
欢迎转载,但请注明「作者」和「原文地址」。转载请在文中保留此段,感谢您对作者版权的尊重。如需商业转载或刊登,请联系作者获得授权。