Reputation: 173
I have written a find function which is like this :
public static List<File> find ( String path, String fName) {
List<File> list = new ArrayList<>() ;
File dir = new File(path) ;
if( dir. isDirectory() ) {
for( String aChild : dir. list()) {
list = find(path + File.separator + aChild, fName) ;
}
}
else {
File[] files = dir. listFiles ( (d, name) -> name. startsWith(fName) && name. endsWith(".txt")) ;
for(File fl : files)
list. add(fl) ;
}
return list;
}
The Directory structure on my Local machine is like C:\Salary with sub directories like January, February etc. Each of the sub directory contains files like 601246_jan_sal.txt or 601246_ feb_sal.txt. I am calling the find function like
List<File> filePath = Utils. find("C:\\Salary\\", "601246") ;
And then performing operation on each individual file.
The problem is that in the find method dir.listFiles(FileNameFilter) is returning null value. What am I doing wrong?
Upvotes: 0
Views: 778
Reputation: 14999
Note that the Files.walk()
method already does what you're trying to do.
public static List<File> find ( String path, String fName) {
List<File> result = new ArrayList<>();
FileVisitor<Path> visitor = new SimpleFileVisitor() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (attrs.isRegularFile()) {
String name = file.getFileName();
if (name.startsWith(fName) && name.endsWith(".txt")) {
result.add(file.toFile());
}
}
return FileVisitResult.CONTINUE;
}
};
Files.walk(Paths.get(path), visitor);
return result;
}
Upvotes: 0
Reputation: 9192
Below is basically the same method with the exception that is uses regex along with the String#matches() method to determine a file name match. I used regex so that the ?
and *
wildcard characters can be used within your file name search criteria, for example:
"601246*.txt"
You may find this useful for other searches you might like to carry out.
There is no returned object with this method, you just need to pass the List to it. Here is an example of how you might use it:
List<File> fileList = new ArrayList<>();
String searchCriteria = "601246*.txt";
searchFolder(new File("C:\\Salary"), searchCriteria, fileList);
// Display found files within the Console Window:
if (!fileList.isEmpty()) {
for (File file : fileList) {
System.out.println(file.getAbsolutePath());
}
}
else {
System.out.println("File name (" + searchCriteria + ") can not be found!");
}
This will search the directory (and all its sub-directories) located at C:\Salary
within the local file system for all files that start with 601246
and ends with .txt
. Since your files are in the following format:
601246_jan_sal.txt or 601246_ feb_sal.txt
and you happen to want all the files for February Sales, your search criteria might be: *feb?sal.txt
.
Here is the searchFolder() method:
/**
* This method navigates through the supplied directory and any
* sub-directories contained within it for the supplied file name search
* criteria. Anything found is placed within the supplied List object.<br>
*
* @param file (File) The starting point directory (folder) in
* the local file system where the file(s) search
* should begin.<br>
*
* @param searchCriteria (String) The name of the file to search for. The
* wildcard characters '?' and '*' can also be used
* within the search criteria. Using an asterisk (*)
* allows you to replace a string of text. This is
* often useful if you know what kind of file you’re
* looking for but don’t know where it is or what
* certain name parts might be. <br><br>
*
* The wildcard '?' lets you use it to replace any character in a search.
* This means that if you’re looking for a file and you’re not sure how it
* is spelled, you can simply substitute '?' for the characters you don’t
* know. In the following example, we search for files that start with
* “img_2” and ends with “.jpg”:<pre>
*
* img_2???.jpg</pre><br>
*
* @param list (List Interface of type File: {@code List<File>})
* This would be the List of File you pass to this
* method. It will be this list that is filled with
* found files objects.<br>
*
* @param ignoreLetterCase (Optional - Boolean) Default is true. The search
* is not letter case sensitive. If false is
* supplied then the search is letter case
* sensitive.
*/
public static void searchFolder(File file, String searchCriteria, List<File> list, boolean... ignoreLetterCase) {
boolean ignoreCase = true;
if (ignoreLetterCase.length > 0) {
ignoreCase = ignoreLetterCase[0];
}
// Convert the supplied criteria string to a Regular Expression
// for the String#matches() method
String regEx = searchCriteria.replace("?", ".").replace("-", ".").replace("*", ".*?");
if (ignoreCase) {
regEx = "(?i)" + regEx;
}
if (file.isDirectory()) {
//System.out.println("Searching directory ... " + file.getAbsoluteFile());
//do you have permission to read this directory?
if (file.canRead()) {
for (File temp : file.listFiles()) {
if (temp.isDirectory()) {
searchFolder(temp, searchCriteria, list, ignoreCase);
}
else {
if (temp.getName().matches(regEx)) {
list.add(temp);
}
}
}
}
else {
System.err.println(file.getAbsoluteFile() + " - PERMISSION DENIED!");
}
}
}
Upvotes: 2
Reputation: 20914
Since you are using recursion, you should pass the list (of files) as a parameter to method find
and not create a new list on each invocation. Hence the method find
does not need to return a value.
public static void find(String path, String fName, List<File> fileList) {
File dir = new File(path);
if (dir.isDirectory()) {
for (File aChild : dir.listFiles()) {
if (aChild.isDirectory()) {
find(aChild.getAbsolutePath(), fName, fileList);
}
else {
String name = aChild.getName();
if (name.startsWith(fName) && name.endsWith(".txt")) {
fileList.add(aChild);
}
}
}
}
else {
String name = aChild.getName();
if (name.startsWith(fName) && name.endsWith(".txt")) {
fileList.add(aChild);
}
}
}
If method parameter path
indicates a directory, then list the files in that directory. For each file in the directory, check whether the name of the file matches your search criteria and if it does then add it to the list. If it doesn't then check if it is itself a directory and if it is then recursively call method find
with the new directory.
Initially call method find
like so
List<File> list = new ArrayList<File>();
find("C:\\Salary\\", "601246", list);
Now list
should contain the list of relevant files. So the following line will print the contents of list
System.out.println(list);
Upvotes: 1
Reputation: 23089
I see a fundamental problem in your logic that answers your question. Here are the key lines along with an explanation:
if( dir.isDirectory() ) {
....
}
else {
File[] files = dir.listFiles(...); // <- "dir" is a file, not a directory
for(File fl : files)
list.add(fl) ;
}
You check if File
object dir
represents a directory. If that test fails (ie: it's not a directory), then you call dir.listFiles
on it and assign the result to files
. But if it's not a directory, then by the definition of that function, it will return null
. It seems that if the check for a directory fails, you should just add the object to list
instead of performing another operation on it.
I think you want this:
if( dir.isDirectory() ) {
....
}
else {
list.add(dir) ;
}
I guess dir
isn't really the right name for the variable here, as it isn't always a directory.
Upvotes: 0