Java: how to get all subdirs recursively? - java

Before debugging the late-hour-out-of-bound-recursive-function: is there a command to get subdirs? giveMeSubDirs(downToPath)?
// WARNING: RECURSION out of bound or too much data
public HashSet<FileObject> getAllDirs(String path) {
HashSet<FileObject> checkedDirs = new HashSet<FileObject>();
HashSet<FileObject> allDirs = new HashSet<FileObject>();
String startingPath = path;
File fileThing = new File(path);
FileObject fileObject = new FileObject(fileThing);
for (FileObject dir : getDirsInDir(path)) {
// SUBDIR
while ( !checkedDirs.contains(dir)
&& !(getDirsInDir(dir.getFile().getParent()).size() == 0)) {
// DO NOT CHECK TOP DIRS if any bottom dir UNCHECKED!
while ( uncheckedDirsOnLevel(path, checkedDirs).size() > 0) {
while (getDirsInDir(path).size() == 0
|| (numberOfCheckedDirsOnLevel(path, checkedDirs)==getDirsInDir(path).size())) {
allDirs.add(new FileObject(new File(path)));
checkedDirs.add(new FileObject(new File(path)));
if(traverseDownOneLevel(path) == startingPath )
return allDirs;
//get nearer to the root
path = traverseDownOneLevel(path);
}
path = giveAnUncheckedDir(path, checkedDirs);
if ( path == "NoUnchecked.") {
checkedDirs.add(new FileObject( (new File(path)).getParentFile() ));
break;
}
}
}
}
return allDirs;
}
Summary about the code:
Go as deep to the directory tree as possible. When there is no dir in a dir, stop, put the dir to the set, traverse up. Do not check dirs in the set.
Stop and return the set if you reach the starting path.
Repeat steps 1 and 2.
PREMISE: the directory-structure is finite and with a small data amount.

You can get all subdirs with the following snippet:
File file = new File("path");
File[] subdirs = file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
});
This gets only immediate subdirs, to retrieve all of them recursively you could write:
List<File> getSubdirs(File file) {
List<File> subdirs = Arrays.asList(file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
}));
subdirs = new ArrayList<File>(subdirs);
List<File> deepSubdirs = new ArrayList<File>();
for(File subdir : subdirs) {
deepSubdirs.addAll(getSubdirs(subdir));
}
subdirs.addAll(deepSubdirs);
return subdirs;
}

No, there is no such functionality in the Java standard API. But there is in Apache commons-io; if you don't want to include it as a library, you could also look at the source code.

Another version with no recursion, and alphabetical order. Also uses a Set to avoid loops (a problem in Unix systems with links).
public static Set<File> subdirs(File d) throws IOException {
TreeSet<File> closed = new TreeSet<File>(new Comparator<File>() {
#Override
public int compare(File f1, File f2) {
return f1.toString().compareTo(f2.toString());
}
});
Deque<File> open = new ArrayDeque<File>();
open.push(d);
closed.add(d);
while ( ! open.isEmpty()) {
d = open.pop();
for (File f : d.listFiles()) {
if (f.isDirectory() && ! closed.contains(f)) {
open.push(f);
closed.add(f);
}
}
}
return closed;
}

The sample code above is missing ");" at the end of the statement.
The correct code should be:
File file = new File("path");
File[] subdirs = file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
}
});

Using recursion:
private void getAllSubFoldersInPath(File path)
{
File[] files=path.listFiles();
try {
for(File file: files)
{
if(file.isDirectory())
{
System.out.println("DIRECTORY:"+file.getCanonicalPath());
getAllSubFoldersInPath(file);
}
else
{
System.out.println("FILE: "+file.getCanonicalPath());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

This is an improved code with Java 8 approach. This code will run on a Recursion basis and find the directories until the last root.
List<File> findAllSubdirs(File file) {
List<File> subdirs = Arrays.asList(file.listFiles(File::isDirectory));
subdirs = new ArrayList<File>(subdirs);
List<File> deepSubdirs = new ArrayList<File>();
for(File subdir : subdirs) {
deepSubdirs.addAll(findAllSubdirs(subdir));
}
subdirs.addAll(deepSubdirs);
return subdirs;
}
If you want only immediate subdirectories list, try with the below line of code..
List<File> subdirs = Arrays.asList(file.listFiles(File::isDirectory));

Get all files from root file as array (#see listFiles)
Sort just for directories by distinguishing between files & directories (#see isDirectory)
Convert (filtered) array from step 1 & 2 to list
Add all found directories to resulting list
Repeat that pattern for each directory file you found in step 1, with growing resulting list
At the end, return resulting list
All that put into some lambda magic:
private static List<File> getAllSubDirectories(File root, List<File> result) {
List<File> currentSubDirs = Arrays.asList(Objects.requireNonNull(root.listFiles(File::isDirectory), "Root file has to be directory"));
result.addAll(currentSubDirs);
currentSubDirs.forEach(file -> getAllSubDirectories(file, result));
return result;
}
Just start with root file (which should be a directory) and an empty list.
Note: Step 1 & 2 can be combined with a filter (#see listFiles(FileFilter filter))

class DirFileFilter extends FileFilter {
boolean accept(File pathname) {
return pathname.isDirectory();
}
}
DirFileFilter filter = new DirFileFilter();
HashSet<File> files = new HashSet<File>();
void rec(File root) {
// add itself to the list
files.put(root);
File[] subdirs = root.list(filter);
// bound of recursion: must return
if (subdirs.length == 0)
return;
else //this is the recursive case: can call itself
for (File file : subdirs)
rec(file);
}

Related

how get a specific file onItemClick from list view

i have a folder with two files: one pdf and one xml.
When i click on folder i want get path of xml file only.
With my code(that i post below) i get paths of both files.
Who can help me?
THANKS!
private ArrayList<String> GetFiles2(File f) {
ArrayList<String> MyFiles = new ArrayList<String>();
//File f = new File(DirectoryPath);
f.mkdirs();
File[] files = f.listFiles();
if (files.length == 0)
return null;
else {
for (int i=0; i<files.length; i++)
MyFiles.add(files[i].getPath());
}
System.out.println("MYFILE:"+MyFiles);
return MyFiles;
}
my result : MYFILE:
`[/storage/emulated/0/ordinazioni/2/23_Agosto_2017_09_44_51_AM.xml,` /storage/emulated/0/ordinazioni/2/23_Agosto_2017_09_44_51_AM.pdf]
You can use the overriden version of File#listFiles(FileFilter) to get specific files from a directory.
File[] files = f.listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".xml");
}
});
Full implementation :
private ArrayList<String> getXmlFiles(File directory) {
ArrayList<String> names = new ArrayList<>();
directory.mkdirs();
File[] files = directory.listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".xml");
}
});
for(File f : files)
names.add(f.getPath());
System.out.println("MYFILE:" + names);
return files.length > 0 ? names : null;
}
Anyway I recommend you to return an empty list insteand of null to avoid NPE issues (Just return names)
You can change your for-loop like this for example:
for (int i=0; i<files.length; i++) {
if(files[i].getPath().endsWith(".xml")) {
MyFiles.add(files[i].getPath());
}
}
this will add to the MyFiles list only the paths of the xml files in the praticular folder
For getting a specific file location use this code,
File dir = Environment.getExternalStorageDirectory();
File yourFile = new File(dir, "path/to/the/file/inside/the/sdcard.ext");
eg ;
"/AAlist/"+serialno.get(position).trim()+".jpg"

how to search for a filename in a list of files

I need to find a file name from the list of filenames and to initiate two methods according to the found result. I tried:
FileList result = service.files().list()
.setPageSize(10)
.setFields("nextPageToken, files(id, name)")
.execute();
List<File> files = result.getFiles();
if (files == null || files.size() == 0) {
System.out.println("No files found.");
} else {
System.out.println("Files:");
for (File file : files) {
System.out.printf("%s (%s)\n", file.getName(), file.getId());
Boolean found = files.contains("XYZ");
if(found)
{
insertIntoFolder();
} else {
createFolder();
}
}
}
I need to find XYZ (the filename) from a list of file names (like sjh, jsdhf, XYZ, ASDF). Once I've found it I need to stop the search. If the name doesn't match the list of names I need to create a folder only once after checking all names from that list.
Boolean found = files.contains("XYZ");
This line is problematic. files is a list of File objects, none of which will match the String "XYX". List.contains() essentially calls Object.equals() on every element of the list, and File.equals("XYZ") will always return false.
If you're programming in an IDE like Eclipse it should show a warning on this line, since it's a bug that can be detected at compile-time.
To determine if a File in a List<File> has a filename matching a given string you need to operate on the filename itself, so the above line should instead be:
boolean found = file.getName().equals("XYZ");
Depending on what exactly you're trying to match you might want to use .getName(), .getAbsolutePath(), or .toString().
It's also a good idea to use the Path API introduced in Java 7, rather than File, which is essentially a legacy class at this point.
If you want a more elegant solution than manually looping over files looking for a match you can use Files.newDirectoryStream(Path, Filter) which allows you to define a Filter predicate that only matches certain files, e.g.
Files.newDirectoryStream(myDirectory, p -> p.getFileName().toString().equals("XYZ"))
File.list(FilenameFilter) is a similar feature for working with File objects, but again, prefer to use the Path API if possible.
Here is a example:
/**
* return true if file is in filesList else return false
*/
static boolean isFileInList(File file, List<File> filesList) {
for(File f: filesList) {
if (f.equals(file)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
List<File> files;// the filelist; make sure assign these two variable.
File file; // the file you want to test.
if (isFileInList(file, files)) {
//file is presented
} else {
//file is not presented
createFolder();
}
}
package test;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
public class DirectoryContents {
public static void main(String[] args) throws IOException {
File f = new File("."); // current directory
FilenameFilter textFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
String lowercaseName = name.toLowerCase();
if (lowercaseName.endsWith(".txt")) {
return true;
} else {
return false;
}
}
};
File[] files = f.listFiles(textFilter);
for (File file : files) {
if (file.isDirectory()) {
System.out.print("directory:");
} else {
System.out.print(" file:");
}
System.out.println(file.getCanonicalPath());
}
}
}

How to add File[] files with specific order

I have small problem with textfilter. Here's my code:
File f = new File("c:\\dir\\");
FilenameFilter textFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
if (name.toLowerCase().startsWith("a")
&& name.toLowerCase().endsWith(".txt")) {
return true;
} else if ((name.toLowerCase().startsWith("b") && name
.toLowerCase().endsWith(".txt"))) {
return true;
} else {
return false;
}
}
};
File[] files = f.listFiles(textFilter);
Now the order in "files" array look's like:
A B
How can I change the order of "files" array that could look's like:
B A
Why I must do that? Becaues I need first read file "B" over the file "A". Could somebody write me any sugestions? Thanks.
The solution is:
Arrays.sort(files, Collections.reverseOrder());
Thanks #Gofoboso

How to list only non hidden and non system file in jtree

File f=new File("C:/");
File fList[] = f.listFiles();
When i use this it list all system file as well as hidden files.
and this cause null pointer exception when i use it to show in jTree like this:
public void getList(DefaultMutableTreeNode node, File f) {
if(f.isDirectory()) {
DefaultMutableTreeNode child = new DefaultMutableTreeNode(f);
node.add(child);
File fList[] = f.listFiles();
for(int i = 0; i < fList.length; i++)
getList(child, fList[i]);
}
}
What should i do so that it do not give NullPointerException and show only non hidden and non system files in jTree?
Do this for hidden files:
File root = new File(yourDirectory);
File[] files = root.listFiles(new FileFilter() {
#Override
public boolean accept(File file) {
return !file.isHidden();
}
});
This will not return hidden files.
As for system files, I believe that is a Windows concept and therefore might not be supported by File interface that tries to be system independent. You can use Command line commands though, if those exist.
Or use what #Reimeus had in his answer.
Possibly like
File root = new File("C:\\");
File[] files = root.listFiles(new FileFilter() {
#Override
public boolean accept(File file) {
Path path = Paths.get(file.getAbsolutePath());
DosFileAttributes dfa;
try {
dfa = Files.readAttributes(path, DosFileAttributes.class);
} catch (IOException e) {
// bad practice
return false;
}
return (!dfa.isHidden() && !dfa.isSystem());
}
});
DosFileAttributes was introduced in Java 7.
If running under Windows, Java 7 introduced DosFileAttributes which enables system and hidden files to be filtered. This can be used in conjunction with a FileFilter
Path srcFile = Paths.get("myDirectory");
DosFileAttributes dfa = Files.readAttributes(srcFile, DosFileAttributes.class);
System.out.println("System File? " + dfa.isSystem());
System.out.println("Hidden File? " + dfa.isHidden());
If you are trying to list all files in C:/ please keep in mind that there are other files also which are neither hidden nor system files, but that still won't open because they require special privileges/permissions. So:
String[] files = file.list();
if (files!=null) {
for (String f : files) open(f);
}
So just compare if the array is null or not and design your recursion in such a way that it just skips those files whose array for the list() function is null.
private void nodes(DefaultMutableTreeNode top, File f) throws IOException {
if (f.isDirectory()) {
File[] listFiles = f.listFiles();
if (listFiles != null) {
DefaultMutableTreeNode b1[] = new DefaultMutableTreeNode[listFiles.length];
for (int i = 0; i < b1.length; i++) {
b1[i] = new DefaultMutableTreeNode(listFiles[i].toString());
top.add(b1[i]);
File g = new File(b1[i].toString());
nodes(b1[i], g);
}
}
}
Here is the code I used to create a window file explorer using jtree.

in java, how to delete one folder/dir by non recursive algorithm?

How can I delete one folder/directory by a non-recursive algorithm in Java? I want to use a non-recursive algorithm in order to avoid StackOverflowErrors when a folder has a very deep path.
Could someone please offer some advise in this area.
In crappy pseudo-code, as I don't have a Java compiler handy to test this:
queue = [ rootDir ]
stack = []
while ( !queue.isEmpty() ) {
currentDir = queue.take()
stack.push( currentDir )
files = currentDir.list()
for ( f : files ) {
if ( f.isDirectory() ) {
queue.add( f )
} else {
f.delete()
}
}
}
while ( !stack.isEmpty() ) {
f = stack.pop()
f.delete()
}
Basically this code should scan a directory, deleting files or queueing subdirectories for further scanning. It places scanned directories in a stack, so that the second while loop deletes them in the correct order (deepest first).
Here's a general way to delete a file/folder :
/**deletes a file/folder recursively, and returns true iff succeeded */
public static boolean deleteQuietly(File file) {
if (file == null || !file.exists())
return true;
if (!file.isDirectory())
return file.delete();
LinkedList<File> dirs = new LinkedList<>();
dirs.add(0, file);
boolean succeededDeletion = true;
while (!dirs.isEmpty()) {
file = dirs.remove(0);
File[] children = file.listFiles();
if (children == null || children.length == 0)
succeededDeletion &= file.delete();
else {
dirs.add(0, file);
for (File child : children)
if (child.isDirectory())
dirs.add(0, child);
else
succeededDeletion &= child.delete();
}
}
return succeededDeletion;
}
this is just a starting point for you to improve on.
The critical part is to find out what's the directories to delete.
This piece of psuedo code should help you to find out all directories under certain directory:
Set<File> allDirectories = new Set<File>();
allDirectories.add(yourStartingDirectory);
while (hasMoreToRead) {
hasMoreToRead = false;
for (File f : allDirectories) {
if (f.isDirectory() && !allDirectories.contains(f)) {
allDirectories.add(f);
hasMoreToRead = true;
}
}
}
This is just a starting point, but you should be able to finish the rest: Avoid revisiting directories in allDirectories that has been processed in previous iterations; Performing delete base on allDirectories; Make the delete more efficient by deleting in "correct" order; etc
// Deletes all files and subdirectories under dir.
// Returns true if all deletions were successful.
// If a deletion fails, the method stops attempting to delete and returns false.
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i=0; i<children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
To remove recursion, you replace the call stack with an explicit stack to hold the items you still need to process. In your case, you keep track of all the parent folders you need to delete after you are done with the current one. Here's an example using a LinkedList as a stack:
public static void rmdir(File dir) {
LinkedList<File> dirs = new LinkedList<File>();
dirs.push(dir);
while (dirs.peek() != null) {
dir = dirs.pop();
File[] contents = dir.listFiles();
if (contents.length == 0) {
dir.delete();
} else {
dirs.push(dir);
for(File content : contents) {
if (content.isDirectory()) {
dirs.push(content);
} else {
content.delete();
}
}
}
}
}
My interpretation of your question is that you want to delete a directory without recursing into the directories within it. In this case, you can implement the deletion using a pretty simple loop...
File directory = new File("c:\\directory_path")
if (!directory.exists()){
return;
}
File[] files = directory.listFiles();
for (int i=0;i<files.length;i++){
if (files[i].isFile()){
boolean deleted = files[i].delete();
if (!deleted){
System.out.println("Problem deleting file " + files[i].getAbsolutePath());
}
}
}
This will list all the Files of the directory in an array, and then loop through them. If the file is a normal file, it will be deleted. Non-normal files, such as directories, will be skipped.
Of course, there are other similar alternatives, such as adding a FileFilter to the listFiles() method so that the array is only populated by normal files, but its effectively pretty similar.
If you want to delete the directory tree, you will have to use some kind of recursion. You could approach it differently though, which might not cause you so many problems, such as building an ArrayList of directories, and then iterating through the ArrayList deleting them one at a time. This would help to reduce the recursion.
public static final void delete(File file) throws IOException
{
if (!file.exists())
throw new IllegalArgumentException("File does not exist: " + file);
if (file.isFile())
{
simpleDelete(file);
return;
}
Deque<File> dirsQueue = new ArrayDeque<File>();
dirsQueue.push(file);
for (File dir; (dir = dirsQueue.peekLast()) != null;)
{
File[] children = dir.listFiles();
if (children == null)
throw new IOException("Unable to read directory: " + dir);
if (children.length == 0)
{
simpleDelete(dir);
dirsQueue.removeLast();
continue;
}
for (File child : children)
{
if (child.isDirectory())
dirsQueue.addLast(child);
else
simpleDelete(child);
}
}
}
private static final void simpleDelete(File file) throws IOException
{
if (!file.delete())
throw new IOException("Unable to delete " + (file.isDirectory() ? "directory" : "file") + ": " + file);
}

Categories