Java File I/O (NIO.2)

Use Path interface to operate on file and directory paths

The Path interface defines an object that represents the path to a file or a directory.

A Path object has a root component and a hierarchical sequence of names separated by backslashes (in Windows) or slashes (Unix/Linux). The root component identifies the file system being used, for example, a drive letter. The names represent the directories needed to navigate to the target file or directory. The last name in the sequence represents the name of the target file or directory.

To create a Path object you can use the the get method of the Paths class like this:

Path p1 = Paths.get("c:\\file.txt"); // using an absolute path
Path p2 = Paths.get("c:\\data", "examples\\file.txt"); // using a relative path to construct the path c:\data\examples\file.txt

When working with relative paths you can use:

  • . to refer to the current directory
  • .. to refer to the parent directory

For example:

Path p1 = Paths.get("c:\\data\\.\\file.txt"); // refers to c:\data\file.txt
Path p2 = Paths.get("c:\\data\\examples\\..\\file.txt"); // refers to c:\data\file.txt

The normalize() method of the Path interface can normalize a path, meaning that it removes all the . and .. in the path and resolves to the real path it refers to.

You can convert a relative path to an absolute path like this:

Path p1 = Paths.get("file.txt");
Path p2 = p1.toAbsolutePath(); // refers for example to c:\data\file.txt

You can convert a Path to a File and a File to a Path like this:

File f = Paths.get("file.txt").toFile();
Path p = new File("file.txt").toPath();

Path stores the name elements as a sequence. The highest element is located at index 0. The following code snippet shows some methods to get information about the path:

Path path = Paths.get("c:\\users\\admin\\file.txt"); 
Path path = path.getFileName(); // returns file.txt
Path path = path.getName(0); // returns users
int count = path.getNameCount(); // returns 3 (users, admin and file.txt)
Path path = path.subpath(0, 2); // returns users/admin
Path path = path.getParent(); // returns /users/admin
Path path = path.getRoot(); // returns c:\ (if we were in a unix filesystem, it would return /)

 

Use Files class to check, read, delete, copy, move, manage metadata of a file or directory

The class Files contains static methods that operate on files and directories.

Check files

The Files.exists() method checks if a given Path exists in the file system, for example

Path path = Paths.get("data/logging.properties");
boolean exists = Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});

The LinkOption parameter is used to indicate how symbolic links are handled if the file is a symbolic link. By default, symbolic links are followed. If the option NOFOLLOW_LINKS is present as in the example, then symbolic links are not followed.

There's also:

static boolean    notExists(Path path, LinkOption... options)

That tests whether the file located by this path does not exist.

Read files

The methods for reading a file are:

public static byte[] readAllBytes(Path path) throws IOException

Reads all the bytes from a file.

public static List<String> readAllLines(Path path,  Charset cs) throws IOException

Read all lines from a file. Bytes from the file are decoded into characters using the specified charset.

public static List<String> readAllLines(Path path)  throws IOException

Read all lines from a file. Bytes from the file are decoded into characters using the UTF-8 charset.

All methods ensure that the file is closed when all bytes have been read or an I/O error, or other runtime exception, is thrown. Also, they are not designed for reading large files. Here's an example

Path path = Paths.get("file.txt");
try {
    List<String> lines = Files.readAllLines(path);
} catch (IOException e) {
    // something failed
    e.printStackTrace();
}

Delete files

The Files.delete() method can delete a file or directory. Here's an example:

Path path = Paths.get("file.txt");
try {
    Files.delete(path);
} catch (IOException e) {
    // deleting failed
    e.printStackTrace();
}

This method will only delete a directory if it is empty.

Copy files

The Files.copy() method copies a file from one path to another. If the destination file already exists, a java.nio.file.FileAlreadyExistsException is thrown:

Path source  = Paths.get("file.txt");
Path target = Paths.get("file-copy.txt");

try {
    Files.copy(source, destination);
} catch(FileAlreadyExistsException fae) {
    fae.printStackTrace();
} catch (IOException e) {
    // something else went wrong
    e.printStackTrace();
}

If you want to overwrite an existing file use this line:

Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);

Move files

Files.move() moves a file. Moving a file is the same as renaming it, but moving an both move it to a different directory and change its name in the same operation. If the destination file already exists, a java.nio.file.FileAlreadyExistsException is thrown:

Path source  = Paths.get("file.txt");
Path target = Paths.get("file-moved.txt");

try {
    Files.move(source, destination);
} catch(FileAlreadyExistsException fae) {
    fae.printStackTrace();
} catch (IOException e) {
    // something else went wrong
    e.printStackTrace();
}

If you want to overwrite an existing file use this line:

Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING);

Manage metadata

The following methods can be used to extract information about the file or directory:

public static boolean isDirectory(Path path, LinkOption... options)

Tests whether a file is a directory.


public static boolean isExecutable(Path path)

Tests whether a file is executable.


public static boolean isHidden(Path path)

Tells whether or not a file is considered hidden.


public static boolean isReadable(Path path)

Tests whether a file is readable.


public static boolean isWritable(Path path)

Tests whether a file is writable.


public static long size(Path path)

Returns the size of a file (in bytes).


public static A <A extends BasicFileAttributes> readAttributes(Path path, Class<A> type, LinkOption... options)

Reads a file's attributes as a bulk operation. The type parameter is the type of the attributes and this method returns an instance of that type if supported. The implementations are:

The options array may be used to indicate how symbolic links are handled. By default, symbolic links are followed.

For example:

BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);

public static Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)

Reads a set of file attributes as a bulk operation. The attributes parameter identifies the attributes to be read and takes the form:

[view-name:]attribute-list

Where view-name is the name of a FileAttributeView that identifies a set of file attributes. If not specified, it defaults to "basic". Some possible values are:
"*" - Read all basic-file-attributes.
"lastModifiedTime,lastAccessTime" - Reads the last modified time and last access time basic attributes.
"posix:*" - Read all POSIX-file-attributes.
"posix:permissions,owner" - Reads the POSX file permissions and owner.

The options parameter may be used to indicate how symbolic links are handled. By default, symbolic links are followed.


public static Object getAttribute(Path path, String attribute, LinkOption... options)throws IOException

Reads the value of a file attribute. The attribute parameter identifies the attribute to be read and takes the form:

[view-name:]attribute-name

The options parameter may be used to indicate how symbolic links are handled. By default, symbolic links are followed.

For example:

int size = (Integer)Files.getAttribute(path, "basic:size");

public static Path setAttribute(Path path, String attribute,  Object value,  LinkOption... options)

Sets the value of a file attribute. The attribute parameter identifies the attribute to be set and takes the form:

[view-name:]attribute-name

The options parameter may be used to indicate how symbolic links are handled. By default, symbolic links are followed.

For example:

Files.setAttribute(path, "dos:hidden", false);

 

Use Stream API with NIO.2

In Java 8, the class Files adds methods that allow us to use streams with files.

List

public static Stream<Path> list(Path dir)

Streams all the elements of a given directory. This method is not recursive. For example:

try (Stream<Path> stream = Files.list(Paths.get("c:\\data"))) {
    stream.map(String::valueOf)
        .filter(path -> path.endsWith(".txt"))
        .forEach(System.out::println);
}

The example maps each path to its string representation, filter the results (only files ending in .txt) and print them. Streams implement AutoCloseable, so the try-with-resources block should be used to ensure that the stream's close method is invoked after the stream operations are completed.

Find and Walk

public static Stream<Path> find(Path start, int maxDepth,  BiPredicate<Path,BasicFileAttributes> matcher, FileVisitOption... options)

Search for files starting at the given Path that represents the root of the file tree with the maximum number of directory levels to search given by the parameter maxDepth. For each file encountered, the given BiPredicate is invoked and the file is only included in the returned Stream if the BiPredicate returns true. For example:

try (Stream<Path> stream = Files.find(Paths.get("c:\\data"), 3, 
                                        (path, attr) -> String.valueOf(path).endsWith(".txt"))) {
    stream.map(String::valueOf).forEach(System.out::println);
}

The example searches for all txt files and print their path and name.

public static Stream<Path> walk(Path start, int maxDepth,  FileVisitOption... options)
public static Stream<Path> walk(Path start, FileVisitOption... options) // This visits all levels of the directory tree.

We can do the same thing but using the walk method and a stream filter:

try (Stream<Path> stream = Files.walk(Paths.get("c:\\data"), 3)) {
    stream.map(String::valueOf).
          filter(path -> path.endsWith(".txt")).
          forEach(System.out::println);
}

However, the find method is more efficient for these cases.

Read lines and BufferedReader

public static Stream<String>    lines(Path path, Charset cs)
public static Stream<String>    lines(Path path) // Uses the UTF-8 charset.

This method reads all lines from a file into a stream. For example:

try (Stream<String> stream = Files.lines(Paths.get("c:\\data\\file.txt"))) {
    stream.map(String::toLowerCase).forEach(System.out::println);
}

BufferedReader also supports stream with its lines method:

try (BufferedReader reader = Files.newBufferedReader(Paths.get("c:\\data\files.txt"))) {
    reader.lines().map(String::toLowerCase).forEach(System.out::println);
}

 

Content