I need to get a list of all the files in a directory, including files in all the sub-directories. What is the standard way to accomplish directory iteration with Java?
You can use File#isDirectory() to test if the given file (path) is a directory. If this is true, then you just call the same method again with its File#listFiles() outcome. This is called recursion.
Here's a basic kickoff example:
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());
}
}
}
}
Note that this is sensitive to StackOverflowError when the tree is deeper than the JVM's stack can hold. If you're already on Java 8 or newer, then you'd better use Files#walk() instead which utilizes tail recursion:
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());
}
}
}
If you are using Java 1.7, you can use java.nio.file.Files.walkFileTree(...).
For example:
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();
}
}
}
If you are using Java 8, you can use the stream interface with 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();
}
}
}
Check out the FileUtils class in Apache Commons - specifically iterateFiles:
Allows iteration over the files in given directory (and optionally its subdirectories).
Using 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());
}
Use false if you do not want files from sub directories.
For Java 7+, there is also https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html
Example taken from the 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;
}
It's a tree, so recursion is your friend: start with the parent directory and call the method to get an array of child Files. Iterate through the child array. If the current value is a directory, pass it to a recursive call of your method. If not, process the leaf file appropriately.
As noted, this is a recursion problem. In particular, you may want to look at
listFiles()
In the java File API here. It returns an array of all the files in a directory. Using this along with
isDirectory()
to see if you need to recurse further is a good start.
You can also misuse File.list(FilenameFilter) (and variants) for file traversal. Short code and works in early java versions, e.g:
// 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;
}
});
To add with #msandiford answer, as most of the times when a file tree is walked u may want to execute a function as a directory or any particular file is visited. If u are reluctant to using streams. The following methods overridden can be implemented
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;
}
});
I like to use Optional and streams to have a net and clear solution,
i use the below code to iterate over a directory. the below cases are handled by the code:
handle the case of empty directory
Laziness
but as mentioned by others, you still have to pay attention for outOfMemory in case you have huge folders
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>
Related
How to rename folder even if the target folder with same name has already existed in the directory.
I have tried the method renameTo of class File,but it does not work.
For example:
/root
/a
/b
I want to rename folder b with the name of folder a, actually the folder a will be replaced,yes,that's what I want.
You need to first delete everything in 'a' or move/rename it to something else.
You can delete it with Apache's recurrsive deleteDictionary function. This gets every file in the aFolder and deletes it, then deletes the folder itself.
FileUtils.deleteDirectory(aFolder);
Then, you can use #renameTo
bFolder.renameTo(aFolder);
In Java 7 and above you can turn to Files for delete and move.
In your case, you can achieve it as follows:
public class HelloWord {
public static void main(String... args) throws Exception {
Path targetPath = Paths.get(Paths.get("").toAbsolutePath().toString().concat("/src/resources/").concat("a"));
Path thePath = Paths.get(Paths.get("").toAbsolutePath().toString().concat("/src/resources/").concat("b"));
if (Files.exists(targetPath)) { // if the target folder exists, delete it first;
deleteFolder(targetPath);
}
Files.move(thePath, targetPath);
}
private static void deleteFolder(Path path) {
try {
if (Files.isRegularFile(path)) { // delete regular file directly;
Files.delete(path);
return ;
}
try (Stream<Path> paths = Files.walk(path)) {
paths.filter(p -> p.compareTo(path) != 0).forEach(p -> deleteFolder(p)); // delete all the children folders or files;
Files.delete(path); // delete the folder itself;
}
} catch (IOException ignored) {
ignored.printStackTrace();
}
}
}
My local test
The structure of the folder for my local test is as follows:
The result:
The solution uses java.nio.file.Files class's move static method.
import java.nio.file.*;
import java.io.IOException;
import java.nio.file.attribute.*;
import java.util.*;
import java.util.stream.*;
public class FilesMoveExample {
public static void main (String [] args)
throws IOException {
Path srcePath = Paths.get("C:\\java-nio2\\folder1");
Path targetPath = Paths.get("C:\\java-nio2\\folder2");
Files.move(srcePath, targetPath); // NOTE: Statement A
}
}
Assume folder1 is source directory and contains multiple sub-directories and files.
Scenario 1:
'Statement A' used as it is.
Source folder1 exists and the target folder2 does not exist.
On running the code, folder1 is renamed to folder2. The folder1's file tree is moved to folder2.
Scenario 2:
'Statement A' modified to: Files.move(srcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
Source folder1 and the target folder2 (an empty directory) exists.
On running the code, the target folder2 is replaced with folder1 (and renamed to folder2). The folder1's file tree is moved to folder2.
Scenario 3:
'Statement A' modified to: Files.move(srcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
Source folder1 and the target folder2 (a non-empty directory) exists.
On running the code, throws DirectoryNotEmptyException.
In this scenario 3 the target directory needs to be empty for the move to complete successfully. So, delete the target directory recursively using one of the following methods, and perform the move. The first uses Java 7 and the next uses Java 8:
private static void deleteUsingWalkFileTree(Path start)
throws IOException {
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException e)
throws IOException {
if (e == null) {
Files.delete(dir);
return FileVisitResult.CONTINUE;
} else {
throw e;
}
}
});
}
private static void deleteUsingWalk(Path start )
throws IOException {
List<Path> files = Files.walk(start)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
for (Path p : files) {
Files.delete(p);
}
}
I am listing all the files names in a given directory( recursively). That includes showing the file names in sub-directories also. How I can restrict it to not to show the files/dir under one specific sub-directory (skip one specific directory)
File file = new File(FILE_PATH);
// Recursively search for all the resource files.
Collection files = FileUtils.listFiles(file, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
for (Iterator iterator = files.iterator(); iterator.hasNext();)
{
File fileIter = (File) iterator.next();
System.out.println("File = " + fileIter.getPath());
}
Java SE has its own method for doing this: Files.walkFileTree. You pass it a FileVisitor (usually a subclass of SimpleFileVisitor), each of whose methods can return a FileVisitResult. To skip a directory, simply return FileVisitResult.SKIP_SUBTREE:
Files.walkFileTree(file.toPath(),
new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attr)
throws IOException {
if (dir.endsWith("forbiddenDir")) {
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attr)
throws IOException {
System.out.println("File = " + file);
return FileVisitResult.CONTINUE;
}
});
The Path class is the modern replacement for the obsolete File class. You should avoid using File, since many of its methods do not report errors properly. If you absolutely need a File object, Path has a toFile() method. Conversely, File has a toPath() method.
Hi guys I'm trying to find if a specific file is inside the project directory.
File f = new File(System.getProperty("user.dir"));
System.out.println(f);
String archivoLiga="LigaV2";
System.out.println(f.listFiles((dir1, name) -> name.startsWith(archivoLiga) && name.endsWith(".properties")).length == 0);
But this only works if the file is in the "first" level, i want it to find it even if it's inside another folder. Any ideas?
Use Java 8's find() method to recurse subdirectories:
final int MAX_DEPTH = 50; // Max depth of subdirectories to search
Path userDir = Paths.get(System.getProperty("user.dir"));
System.out.println(userDir);
String archivoLiga="LigaV2";
System.out.println(
Files.find(
userDir,
maxDepth,
(path,attr) -> path.getFileName().startsWith(archivoLiga)
&& path.getFileName().endsWith(".properties"))
.findAny()
.isPresent());
Try recursively looking inside subfolders :-
public boolean checkForFile(String dirname,String prefix,String ext){
File dir = new File(dirname);
//System.out.println(dir);
for(File f : dir.listFiles()){
if(f.isFile()){
if(f.getName().startsWith(prefix) && f.getName().endsWith(ext)){
System.out.println(f.getName());
return true;
}
}
else{
//This step starts looking inside subfolder as well
return checkForFile(f.getAbsolutePath(),prefix,ext);
}
}
return true;
}
To check a file inside a folder, You will need to use exists() method of java.io.File like this:
boolean exists = new File("FOLDER_PATH/FILE_NAME").exists();
if (exists) {
System.out.println("File exists inside given folder");
} else {
System.out.println("File does not exists inside given folder");
}
Also possible with FileVisitor:
#Getter #Setter
#RequiredArgsConstructor
public static class SearchVisitor extends SimpleFileVisitor<Path> {
private final String fileToSearch;
private boolean found=false;
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if(!file.getFileName().toString().equals(fileToSearch)) return FileVisitResult.CONTINUE;
found=true;
return FileVisitResult.TERMINATE;
}
}
public void test() throws IOException {
SearchVisitor sv = new SearchVisitor("LigaV2");
Files.walkFileTree( Paths.get(System.getProperty("user.dir")), sv);
log.info("found file {}:{}", sv.getFileToSearch(), sv.isFound());
}
Use public boolean isDirectory() in java file API following is the link to oracle documentation. Tests whether the file denoted by this abstract pathname is a directory.
https://docs.oracle.com/javase/7/docs/api/java/io/File.html#isDirectory()
I asked a question about how to delete all files from folders in a directory but keep the folders, this can be found here:
How to delete files of a directory but not the folders
One of the purposed solutions was to use recursion, to achieve this:
public void DeleteFiles() {
File file =
new File(
"D:/Documents/NetBeansProjects/printing~subversion/fileupload/web/"+
"resources/pdf/");
System.out.println("Called deleteFiles");
if (file.isDirectory()) {
for (File f : file.listFiles()) {
DeleteFiles();
}
} else {
file.delete();
}
}
However I just get a console full of Called deleteFiles, until I get the stack overflow error, it does not seem to go through the directory to find files and delete them, how can I achieve this?
Recursion is asking for trouble when there are much simpler solutions. With commons-io:
import java.io.File;
import org.apache.commons.io.FileUtils;
import static org.apache.commons.io.filefilter.TrueFileFilter.TRUE;
File root = new File("D:/Documents/NetBeansProjects/printing~subversion/fileupload/web/resources/pdf/");
Iterator<File> files = FileUtils.iterateFiles(root, TRUE, TRUE);
for (File file : files) {
file.delete();
}
or with JDK 7:
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
Path root = Paths.get("D:/Documents/NetBeansProjects/printing~subversion/fileupload/web/resources/pdf/");
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
file.delete();
return FileVisitResult.CONTINUE;
}
})
public void DeleteFiles() {
File file = new File("D:/Documents/NetBeansProjects/printing~subversion/fileupload/web/resources/pdf/");
System.out.println("Called deleteFiles");
DeleteFiles(file);
}
public void DeleteFiles(File file) {
if (file.isDirectory()) {
for (File f : file.listFiles()) {
DeleteFiles(f);
}
} else {
file.delete();
}
}
File file = new File("D:/Documents/NetBeansProjects/printing~subversion/fileupload/web/resources/pdf/");
You are creating the same file again and again. Declare that file outside the function.
Your recursion is different from the suggested one.
public void DeleteFiles( File file) {
System.out.println("Called deleteFiles");
if (file.isDirectory()) {
for (File f : file.listFiles()) {
DeleteFiles(f);
}
} else {
file.delete();
}
}
Here is the code I have thus far:
import java.io.*;
class JAVAFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.endsWith(".java"));
}
}
public class tester {
public static void main(String args[])
{
FilenameFilter filter = new JAVAFilter();
File directory = new File("C:\\1.3\\");
String filename[] = directory.list(filter);
}
}
At this point, it'll store a list of all the *.java files from the directory C:\1.3\ in the string array filename. However, i'd like to store a list of all the java files also in subdirectories (preferably with their path within C:\1.3\ specified also. How do I go about doing this? Thanks!
you should look at DirectoryWalker from Apache
I'm afraid you can't do it with the list(FilenameFilter) method. You'll have to list all files and directories, and then do the filtering yourself. Something like this:
public List<File> getFiles(File dir, FilenameFilter filter) {
List<File> ret = new ArrayList<File>();
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
ret.addAll(getFiles(f, filter));
} else if (filter.accept(dir, f.getName())) {
ret.add(f);
}
}
return ret;
}
As far as I know, you will have to do this manually (recursively), i.e. you will have to call list(filter) for all sub-directories of C:\1.3\, and so on....