Accessing files and directories in java

Accessing files and directories in java

The conversion of a pathname string to or from an abstract pathname is inherently system-dependent. When an abstract pathname is converted into a pathname string, each name is separated from the next by a single copy of the default separator character. The default name-separator character is defined by the system property file.separator , and is made available in the public static fields separator and separatorChar of this class. When a pathname string is converted into an abstract pathname, the names within it may be separated by the default name-separator character or by any other name-separator character that is supported by the underlying system.

A pathname, whether abstract or in string form, may be either absolute or relative. An absolute pathname is complete in that no other information is required in order to locate the file that it denotes. A relative pathname, in contrast, must be interpreted in terms of information taken from some other pathname. By default the classes in the java.io package always resolve relative pathnames against the current user directory. This directory is named by the system property user.dir , and is typically the directory in which the Java virtual machine was invoked.

The parent of an abstract pathname may be obtained by invoking the getParent() method of this class and consists of the pathname’s prefix and each name in the pathname’s name sequence except for the last. Each directory’s absolute pathname is an ancestor of any File object with an absolute abstract pathname which begins with the directory’s absolute pathname. For example, the directory denoted by the abstract pathname «/usr» is an ancestor of the directory denoted by the pathname «/usr/local/bin» .

  • For UNIX platforms, the prefix of an absolute pathname is always «/» . Relative pathnames have no prefix. The abstract pathname denoting the root directory has the prefix «/» and an empty name sequence.
  • For Microsoft Windows platforms, the prefix of a pathname that contains a drive specifier consists of the drive letter followed by «:» and possibly followed by «\\» if the pathname is absolute. The prefix of a UNC pathname is «\\\\» ; the hostname and the share name are the first two names in the name sequence. A relative pathname that does not specify a drive has no prefix.
Читайте также:  Python current date minus month

Instances of this class may or may not denote an actual file-system object such as a file or a directory. If it does denote such an object then that object resides in a partition. A partition is an operating system-specific portion of storage for a file system. A single storage device (e.g. a physical disk-drive, flash memory, CD-ROM) may contain multiple partitions. The object, if any, will reside on the partition named by some ancestor of the absolute form of this pathname.

A file system may implement restrictions to certain operations on the actual file-system object, such as reading, writing, and executing. These restrictions are collectively known as access permissions. The file system may have multiple sets of access permissions on a single object. For example, one set may apply to the object’s owner, and another may apply to all other users. The access permissions on an object may cause some methods in this class to fail.

Instances of the File class are immutable; that is, once created, the abstract pathname represented by a File object will never change.

Interoperability with java.nio.file package

The java.nio.file package defines interfaces and classes for the Java virtual machine to access files, file attributes, and file systems. This API may be used to overcome many of the limitations of the java.io.File class. The toPath method may be used to obtain a Path that uses the abstract path represented by a File object to locate a file. The resulting Path may be used with the Files class to provide more efficient and extensive access to additional file operations, file attributes, and I/O exceptions to help diagnose errors when an operation on a file fails.

Читайте также:  Timestamp to datetime kotlin

Источник

How to use Java NIO API

In this article, we will learn how to use Java NIO API such as accessing files and directories, reading and writing text files, … They may be useful to improve performance in our project.

Table of contents

Accessing Files and Directories using Java NIO Path

  1. Introduction to Path class In the JDK 1.0, 1995 — 1996, we have a old File object. And now we have a second object that is used to interact with files and directories called as Path object. Path is an interface which is introduced in Java NIO file package during Java version 7, and is the representation of location in particular file system.
  2. The difference between Files and Paths
    • File is a class from Java 1.0 that models files, whereas Path is an interface from Java 7. It means that for each file system, for each type of file system that exists, we can have different implementation of the Path interface. The implementation for the Windows file system is not the same as Linux file system. For instance, since a File object is created on a String. It’s just a wrapper on the String that represent the path of the file system. It’s independent on any file system, and indeed, a file does not know about anything the file system. But Java 7 introduces a FileSystem class with file system objects. And path is necessarily to linked to an explicit file system. If we do not tell the system, which file system we’re going to use when we create a path, this system will give us a path and linked to our default file system, which is most of the time the case.
  3. Accessing Files with Path objects Basically, we can do the same kind of thing as with the File object. We can access file in roughly the same way, but we can get more information, especially specific attributes dependent on our FileSystem objects. The Path is just used to access file or directory on the given file system. A Path gives information on a path:
    • different elements directory that we are going to go through to access this file.
    • We can check if this element contains a symbolic link or not.
    • Through different factory methods from Files class, we can have more information on the file or directory represented by this path. Basically, the read/write property, can be executed in this file or directories exist or not.
  4. Creating Path object Due to Path is an interface, to get an instances of Path, we have to use factory method. Now, we have two pattern to create those Path instances.
    • from the Paths factory class. From the Paths factory class, there are two get() methods
      • takes a path as a String, or a vararg of paths. For example:
 Path path1 = Paths.get("C:/tmp/debug.log"); Path path2 = Paths.get("C:", "tmp", "debug.log"); 
 URI uri = URI.create("file://C:/tmp/debug.log"); Path path3 = Paths.get(uri); 
 Path path3 = Path.of("C:/tmp/debug.log"); 
  • Check if it exists or not.
  • Check if it is hidden.
  • Check if the path is a file or a directory.
  • Check if it is readable or writeable.
  • Check if it is executable.

All those functionalities are implemented using corresponding method. For instance, we have exists() method that takes a path as a parameter.

 Path path = Paths.get("C:/tmp/debug.log"); boolean exists = Files.exists(path); boolean exists = Files.exists(path, LinkOption.NOFOLLOW_LINKS); 

These methods may take a further argument which is a constant called as NOFOLLOW_LINKS . It means that the API can check if a path contains symbolic links.

And we can check if two path actually locate the same file. Those two path may be very different in the structure. But, in fact, pointing to the same element on the disk, and this is what this method is used to in this case.

 boolean sameFile = Files.isSameFile(path1, path2); 

Reading and Writing Text files using Java NIO API

  1. Introducing the possible errors when dealing with text files The difference between Binary file and Text file
    • Binary files are about storing bytes. For instance, if our bytes have to be grouped four by four to create integer, this is our interpretation, our application’s specific interpretation of the binary content of a given file.
    • Text files are about storing characters. The character is also a set of bytes in a text file, but in can be interpreted as this or that character using a Charset. There are many Charset such as ASCII, ISO 8859, UTF-8, Unicode, … The charset we should all be using is probably utf-8, but this is not the only one that is living around. So the API provide support for that.

Java provides classes to read and write both types of files.

  • The classes are provided to read and write text file such as Reader, Writer, and the further extended classes …
  • The classes for binary files are InputStream, OutputStream, …
  • get a BufferedReader — is extension of Reader class.
  • then, through this BufferedReader, we are going to read file line by line.

Belows are some steps to use the above pattern:

  • create a Path object.
  • get a BufferedReader on the corresponding file.
  • read a line (return null if there is none)
 Path path = Paths.get("C:/tmp/sometext.txt"); BufferedReader reader = Files.newBufferedReader(path); String line = reader.readLine(); 

Some problems can happens in the above case:

Then, it can throw an exception like this:

 Exception in thread "main" java.nio.charset.MalformedInputException: Input length = 1 

When we see this exception, we can find that the charset we need is not a default charset, we need to pass charset as a parameter.

 BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.ISO_8859_1); 
 Path path = Paths.get("C:/tmp/sometext.txt"); BufferedWriter writer = Files.newBufferedWriter(path); writer.write("Hello, world!"); 

Some problems for the above code:

We need to call close() method of BufferedWriter instance manually or use try-with-resources to automatically close this file. Because the close() method is calling a flush() method itself. And the flush() method will transfer what has been returned to the disk.

In order to use multiple writer correctly, we need to put writers into try statement like the below code.

 Path path = Path.of(". "); try (BufferedWriter writer1 = Files.newBufferedWriter(path); BufferedWriter writer2 = Files.newBufferedWriter(writer1); PrintWriter pw = new PrintWriter(writer2);)  writer1.write("Hello, world. "); pw.printf("\n"); > catch (IOException ex)  System.out.println("Exception: " + ex); > 

Manipulating files on a File System using the Java NIO API

  1. Moving files around It uses the Files factory methods:
    • create: file, directory, symbolic link.
    • delete
    • copy: can copy an input/output stream
    • move
  2. Finding a File in a set of Directories Now supposed that we’re looking for a specific file or a specific set of files, category of files in a given hierarchy of directories, we have thousand of files under directory and we want to find special files among them. We have one pattern for that, a static factory method. From this Files factory class which is called the find() method. The find() method has some parameters:
    • a path which is the root of the directory under which we want to look for information.
    • an integer that is the depth of exploration under this route.
    • an element which is a Predicate, takes a Path under set of attributes. This path represents every elements. This API is going to visit under this hierarchy of directory. So this Path can represent a file or it can represent a directory. The set of attributes will give us information of this path. Is it a file or directory? created date, modification date, owner, the last person modified it, …

For example, find all images in a specific folder.

 Path dir = Paths.get("E:/images"); Path image = Paths.get("image.jpg"); Files.find(dir, 3, (path, attributes) -> path.endsWith(image)); 

For example, find images that have been created between two hours ago and now.

 Path dir = Path.of("E:/images"); Instant twoHoursAgo = Instant.now().minus(Duration.ofHours(2)); Files.find(dir, 3, (path, attributes) ->  attributes.creationTime().toInstant().isAfter(twoHoursAgo); >); 
  • walkFileTree It takes a root directory as a parameter and uses a FileVisitor which is a callback that is going to be called for any elements found in this hierarchy of directory.
  • walk It starts with a path and returns a **Stream ** of all the files and directories. Instead of visiting every element using a callback, it will provide all the elements in a streamer lazily computer so that we don’t lose any kind of performance, and we’re going to process the content of these directory with the map, filter, reduce approach.

Источник

Оцените статью