Use NIO for Path and File System Operations
At the
beginning of Chapter 20, the File
class in the java.io package was
examined. As explained there, the File
class deals with the file system and with the various attributes associated
with a file, such as whether a file is read-only, hidden, and so on. It was
also used to obtain information about a file’s path. Although the File class is still perfectly
acceptable, the interfaces and classes defined by NIO.2 offer a better way to
perform these functions. The benefits include support for symbolic links,
better support for directory tree traversal, and improved handling of metadata,
among others. The following sections show samples of two common file system
operations: obtaining information about a path and file and getting the
contents of a directory.
Obtain Information About a Path and a File
Information
about a path can be obtained by using methods defined by Path. Some attributes associated with the file described by a Path (such as whether or not the file
is hidden) are obtained by using methods defined by Files. The Path methods
used here are getName( ), getParent( ), and toAbsolutePath( ). Those provided by Files are isExecutable( ), isHidden( ), isReadable( ),
isWritable( ), and exists( ).
These are summarized in Tables 21-3 and
21-4, shown earlier.
Other
file attributes are obtained by requesting a list of attributes by calling Files.readAttributes( ). In the
program, this method is called to obtain the BasicFileAttributes associated with a file, but the general
approach applies to other types of attributes.
The
following program demonstrates several of the Path and Files methods,
along with several methods provided by BasicFileAttributes.
This program assumes that a file called test.txt
exists in a directory called
examples, which must be a subdirectory of the current directory.
//Obtain information about a path and a file.
//Requires JDK 7 or later.
import
java.io.*; import java.nio.file.*;
import
java.nio.file.attribute.*;
class
PathDemo {
public
static void main(String args[]) {
Path
filepath = Paths.get("examples\\test.txt");
System.out.println("File
Name: " + filepath.getName(1));
System.out.println("Path:
" + filepath);
System.out.println("Absolute
Path: " + filepath.toAbsolutePath());
System.out.println("Parent:
" + filepath.getParent());
f(Files.exists(filepath))
System.out.println("File exists");
else
System.out.println("File
does not exist");
try {
if(Files.isHidden(filepath))
System.out.println("File
is hidden"); else
System.out.println("File
is not hidden"); } catch(IOException e) {
System.out.println("I/O
Error: " + e);
}
Files.isWritable(filepath);
System.out.println("File
is writable");
Files.isReadable(filepath);
System.out.println("File is readable");
try {
BasicFileAttributes
attribs =
Files.readAttributes(filepath,
BasicFileAttributes.class);
if(attribs.isDirectory())
System.out.println("The file is a directory");
else
System.out.println("The
file is not a directory");
if(attribs.isRegularFile())
System.out.println("The file is a normal file");
else
System.out.println("The
file is not a normal file");
if(attribs.isSymbolicLink())
System.out.println("The
file is a symbolic link"); else
System.out.println("The
file is not a symbolic link");
System.out.println("File
last modified: " + attribs.lastModifiedTime());
System.out.println("File
size: " + attribs.size() + " Bytes");
}
catch(IOException e) {
System.out.println("Error
reading attributes: " + e);
}
}
If you
execute this program from a directory called MyDir, which has a subdirectory called examples, and the examples directory
contains the test.txt file, then you
will see output similar to that
shown here. (Of course, the information you see will differ.)
File Name: test.txt
Path: examples\test.txt
Absolute Path:
C:\MyDir\examples\test.txt
Parent: examples
File exists
File is not hidden
File is writable
File is readable
The file is not a directory
The file is a normal file
The file is not a symbolic
link
File last modified:
2014-01-01T18:20:46.380445Z
File size: 18 Bytes
If you
are using a computer that supports the FAT file system (i.e., the DOS file
system), then you might want to try using the methods defined by DosFileAttributes. If you are using a
POSIX-compatible system, then try using PosixFileAttributes.
List the Contents of a Directory
If a
path describes a directory, then you can read the contents of that directory by
using static methods defined by Files. To do this, you first obtain a
directory stream by calling
newDirectoryStream( ), passing in a
Path that describes the directory. One form of newDirectoryStream( ) is shown here:
static
DirectoryStream<Path> newDirectoryStream(Path dirPath) throws IOException
Here, dirPath encapsulates the path to the
directory. The method returns a DirectoryStream<Path>
object that can be used to obtain the contents of the directory. It will throw an IOException if an I/O error occurs and a NotDirectoryException (which is a subclass of IOException) if the specified path is not a directory. A SecurityException is also possible if
access to the directory is not permitted.
DirectoryStream<Path> implements AutoCloseable, so it can be managed by
a try-with-resources statement. It
also implements Iterable<Path>.
This means that you can obtain the contents of the directory by iterating over
the DirectoryStream object. When
iterating, each directory entry is represented by a Path instance. An easy way to iterate over a DirectoryStream is to use a for-each style for loop. It is important to understand, however, that the iterator
implemented by DirectoryStream<Path>
can be obtained only once for each instance. Thus, the iterator( ) method can be called only once, and a for-each loop can
be executed only once.
The
following program displays the contents of a directory called MyDir:
// Display a directory.
Requires JDK 7 or later.
import java.io.*; import
java.nio.file.*;
import
java.nio.file.attribute.*;
class DirList {
public static void main(String
args[]) {
String dirname =
"\\MyDir";
// Obtain and manage a
directory stream within a try block.
try (
DirectoryStream<Path> dirstrm =
Files.newDirectoryStream(Paths.get(dirname))
)
{
System.out.println("Directory
of " + dirname);
//Because DirectoryStream implements Iterable,
we
//can use a "foreach" loop to display
the directory.
for(Path entry : dirstrm) {
BasicFileAttributes attribs =
Files.readAttributes(entry,
BasicFileAttributes.class);
if(attribs.isDirectory())
System.out.print("<DIR> ");
else
System.out.print(" ");
System.out.println(entry.getName(1));
}
} catch(InvalidPathException
e) { System.out.println("Path Error " + e);
} catch(NotDirectoryException
e) { System.out.println(dirname + " is not a directory.");
} catch (IOException e) {
System.out.println("I/O Error: " + e);
}
}
}
Here is
sample output from the program:
Directory of \MyDir
DirList.class DirList.java
<DIR> examples
Test.txt
You can
filter the contents of a directory in two ways. The easiest is to use this
version of newDirectoryStream( ):
static
DirectoryStream<Path> newDirectoryStream(Path dirPath, String wildcard)
throws IOException
In this
version, only files that match the wildcard filename specified by wildcard will be obtained. For wildcard, you can specify either a
complete filename or a glob. A glob is a string that defines a general
pattern that will match one or more files using the familiar * and ? wildcard characters.
These
match zero or more of any character and any one character, respectively. The
following are also recognized within a glob.
You can
specify a * or ? character, using \*
and \?. To specify a \, use \\. You can experiment with a glob by substituting this call to newDirectoryStream( ) into the previous
program:
Files.newDirectoryStream(Paths.get(dirname),
"{Path,Dir}*.{java,class}")
This
obtains a directory stream that contains only those files whose names begin
with either "Path" or "Dir" and use either the
"java" or "class" extension. Thus, it would match names
like DirList.java and PathDemo.java, but not MyPathDemo.java, for example.
Another
way to filter a directory is to use this version of newDirectoryStream( ):
static
DirectoryStream<Path> newDirectoryStream(Path dirPath, DirectoryStream.Filter<? super Path> filefilter)
throws
IOException
Here, DirectoryStream.Filter is an interface
that specifies the following method: boolean accept(T entry) throws IOException
In this
case, T will be Path. If you want to include entry
in the list, return true. Otherwise,
return false. This form of newDirectoryStream( ) offers the
advantage of being able to filter a directory based on something other than a
filename. For example, you can filter based on size, creation date,
modification date, or attribute, to name a few.
The
following program demonstrates the process. It will list only those files that
are writable.
// Display a directory of
only those files that are writable.
import java.io.*; import
java.nio.file.*;
import
java.nio.file.attribute.*;
class DirList {
public static void
main(String args[]) { String dirname = "\\MyDir";
// Create a filter that
returns true only for writable files.
DirectoryStream.Filter<Path>
how = new DirectoryStream.Filter<Path>() {
public boolean accept(Path
filename) throws IOException {
if(Files.isWritable(filename))
return true;
return false;
}
};
// Obtain and manage a
directory stream of writable files.
try
(DirectoryStream<Path> dirstrm =
Files.newDirectoryStream(Paths.get(dirname),
how) )
{
System.out.println("Directory
of " + dirname);
for(Path entry : dirstrm) {
BasicFileAttributes attribs =
Files.readAttributes(entry,
BasicFileAttributes.class);
if(attribs.isDirectory())
System.out.print("<DIR> ");
else
System.out.print(" ");
System.out.println(entry.getName(1));
}
} catch(InvalidPathException
e) { System.out.println("Path Error " + e);
} catch(NotDirectoryException
e) { System.out.println(dirname + " is not a directory.");
} catch (IOException e) {
System.out.println("I/O Error: " + e);
}
}
}
Use walkFileTree( ) to List a Directory Tree
The
preceding examples have obtained the contents of only a single directory.
However, sometimes you will want to obtain a list of the files in a directory
tree. In the past, this was quite a chore, but NIO.2 makes it easy because now
you can use the walkFileTree( )
method defined by Files to process a
directory tree. It has two forms. The one used in this chapter is shown here:
static
Path walkFileTree(Path root,
FileVisitor<? extends Path> fv)
throws IOException
The path
to the starting point of the directory walk is passed in root. An instance of FileVisitor
is passed in fv. The implementation of FileVisitor determines how the
directory tree is traversed, and it
gives you access to the directory information. If an I/O error occurs, an IOException is thrown. A SecurityException is also possible.
FileVisitor is an interface that defines how files are
visited when a directory tree is traversed.
It is a generic interface that is declared like this:
interface
FileVisitor<T>
For use
in walkFileTree( ), T will be Path (or any type derived from Path).
FileVisitor defines the following
methods.
Notice that each method returns a FileVisitResult. This enumeration defines the following values:
In
general, to continue traversing the directory and subdirectories, a method
should return
CONTINUE. For
preVisitDirectory( ), return
SKIP_SIBLINGS to bypass the directory and its siblings and prevent postVisitDirectory(
) from being called. To bypass just the directory and subdirectories,
return SKIP_SUBTREE. To stop the
directory traversal, return
TERMINATE.
Although
it is certainly possible to create your own visitor class that implements these
methods defined by FileVisitor, you
won’t normally do so because a simple implementation is provided by SimpleFileVisitor. You can just
override the default implementation of the method or methods in which you are
interested. Here is a short example that illustrates the process. It displays
all files in the directory tree that has \MyDir
as its root. Notice how short this program is.
//A simple example that uses walkFileTree( ) to
display a directory tree.
//Requires JDK 7 or later.
import java.io.*; import
java.nio.file.*;
import
java.nio.file.attribute.*;
//Create a custom version of SimpleFileVisitor
that overrides the visitFile( ) method.
class MyFileVisitor extends
SimpleFileVisitor<Path> {
public FileVisitResult
visitFile(Path path, BasicFileAttributes attribs)
throws IOException
{
System.out.println(path);
return
FileVisitResult.CONTINUE;
}
}
class DirTreeList {
public static void main(String
args[]) { String dirname = "\\MyDir";
System.out.println("Directory
tree starting with " + dirname + ":\n");
try {
Files.walkFileTree(Paths.get(dirname),
new MyFileVisitor());
} catch (IOException exc) {
System.out.println("I/O Error");
}
}
}
Here is
sample output produced by the program when used on the same MyDir directory shown earlier. In this
example, the subdirectory called examples
contains one file called MyProgram.java.
Directory tree starting with
\MyDir:
\MyDir\DirList.class
\MyDir\DirList.java
\MyDir\examples\MyProgram.java
\MyDir\Test.txt
In the program, the class MyFileVisitor extends SimpleFileVisitor, overriding only the visitFile( ) method. In this example, visitFile( ) simply displays the files, but more sophisticated functionality is easy to achieve. For example, you could filter the files or perform actions on the files, such as copying them to a backup device. For the sake of clarity, a named class was used to override visitFile( ), but you could also use an anonymous inner class.
One last
point: It is possible to watch a directory for changes by using java.nio.file.WatchService.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.