Got a requirement in your Java application that calls for a file search feature? If so, then you're in luck.
I'll show you how to do it here with Java NIO.
Even better: I'll show you how to do it with very concise code.
Start With a Glob
Ever heard of a glob? If you haven't, you shouldn't be embarrassed.
Lots of developers crank out software for years without hearing about the concept of a glob.
But you know what it is even if you don't know what it is.
It's a pattern using wildcards to specify filenames. So *.java is a glob.
Why is it called a glob? The answer to that question harkens back to the old UNIX days. The word itself is shorthand for "global command."
It's kind of a kissing cousin to regex or regular expression.
And I'm going to use it in this guide.
Padding Your Code With PatternMatcher
Here's another one that might be new to you: PatternMatcher.
That interface gets used by objects that handle match operations on paths. You typically get an implementation with the aid of a glob, like this:
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**/contact.properties");
And in fact, that's exactly how I'll instantiate a PathMatcher object in this guide.
The way it's handled above, the code grabs the default file system and gets the PatchMatcher implementation using glob syntax.
Note that the code specifies the glob as a string prefaced with "glob:". That's because PathMatcher supports a variety of syntaxes and patterns.
I could have used a regular expression instead of a glob, for example.
The glob itself follows a pattern you're probably familiar with. The asterisk indicates a wildcard of one or more characters.
There are two asterisks in the code above because that's how to search for a match across different path segments.
The Actual Code
Now that we've got the preliminaries out of the way, let's put this one to bed.
Suppose the requirement here is to search for a file called contact.properties in the /etc/careydevelopment directory. The code will look something like this:
final String directory = "/etc/careydevelopment"; final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**/contact.properties"); List<Path> path = Files.walk(Paths.get(directory)).filter(matcher::matches).collect(Collectors.toList());
The first line establishes the directory for searching. That's pretty straightforward.
But keep in mind: it's the parent directory. In other words, the code above will search all subdirectories as well.
I've already explained the second line of code in the previous section.
That brings us to the third line of code, where a lot of stuff is happening. I told you I'd deliver a succinct solution.
The Paths.get() portion of that long line gets the specified directory as a Path object.
Next, Files.walk() traverses the directory structure starting at the Path object. It returns a Stream of Path objects representing all the files and subdirectories it finds.
The filter() part of the line uses the PathMatcher object to weed out any files that don't match the glob.
Finally, the collect() method puts all the matched files into a List of Path objects.
Note: if you're certain that you'll only find at most one (1) file that matches the glob, you could use findFirst() instead of collect() as your terminal operation. Like this:
Optional<Path> path = Files.walk(Paths.get(directory)).filter(matcher::matches).findFirst();
You'll have to use Optional there because there might not be any matches.
Note that if you change the directory to /etc instead of /etc/careydevelopment, you'll get the same results if you run the code above. That's because it will search the whole tree for a matching file.
Wrapping It Up
That was fairly easy, wasn't it?
Now you know how to use Java NIO to search for a file in a directory. The solution above will also search all subdirectories.
It's up to you to take what you've learned here and make it your own. Test it out with your own starting point directory and a glob that delivers results.
Photo by from