Saturday, October 23, 2021

Exploring File and Files Classes and Path Interface

Chapters

Pathnames

Before we discuss File and Files Class and Path Interface, we need to understand pathnames first. Pathnames can be relative or absolute. Absolute pathnames start with the "root" directory. In UNIX or UNIX-like systems, the root directory is "/". For example, /home or /home/user
On Windows, the root directory is "drive-name:\". For example, C:\Users\ or C:\Users\user1\Desktop

Relative pathnames are relative to a working or current directory. For example, On windows, you're using command prompt and the current directoy where the command prompt is working on is in C:\Users\user1\Desktop\folder1. Then you want to delete a text file named as "txt1.txt" in folder1, since your working directory is folder1, just type this command: del txt1.txt
"del" is the command and "txt1.txt" is the relative pathname.

As you can see, relative pathname doesn't need to start from the root directory/file. For example, If your working directory is in C:\Users\user1\Desktop and you want to delete a text file named as "txt1.txt" in folder1 then, type this command: del folder1\txt1.txt

Note: Forward(/) and back(\) slashes are used in different types of pathnames. In UNIX, UNIX-like systems or URLs, forward(/) slash is used. On windows, back(\) slash is used. Though, forward(/) slash may work with some Windows systems.

"." and ".." Symbols

"." represents current directory whereas ".." represents parent directory. "." is more useful for UNIX than Windows System. "." is used with UNIX to do some operation. For example, if we want to run executable in a current directory, we need to use "." to specify the current directory. UNIX command shell doesn't look for executables in current directory for security reasons, I think.

We can use the ".." to move down to a parent directory. For example, on Windows, the pathname "C:\Users\User1\Desktop\folder1\.." is equivalent to "C:\Users\User1\Desktop". You can try this example on the command prompt using the "cd" command.

File Class

The File class is a class that is located in java.io package. It manages file interactions between java application and computers. We can use this class to delete, rename and get the path of a file in abstract or string form. In this tutorial we will discusss some methods that we can use to interact with files in our hard disk/drive.

File Class Constructors

File class has four constructors. These two are commonly used. For the sake of simplicity, we will use this constructor in this tutorial.

Separator Fields

The File class has static fields that represent separators. There are two types of separators: separator and path separator. Separator or the forward(/) slash for UNIX and back(\) slash for Windows, separates directories in a path. path separator or colon(:) for UNIX and semi-colon(;) for Windows, separates path to create a list of paths. Separators in java can be in char or String form.

e.g.
String path = "C:"+File.separator+"Users"+File.separator+"User1";

Note: Separator fields are system-dependent. It means, the separators that the fields return are based on operating system. For example, on Windows, File.separator returns back(\) slash.

exists() Method

This method returns true if the file or directory denoted by this abstract pathname exists. Otherwise, returns false. Hidden files don't affect the functionality of this method.
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    //Backslash(\) has special meaning in
    //java so, we need to escape it by 
    //adding another backslash
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists())
      System.out.println("File Exists!");
    else
      System.out.println("File Doesn't Exist!");
  }
}

Result
File Exists!
If you have a "test" directory in C: drive and there's a "test.txt" in that directory then, file.exists() would return true if no exception occurs. Otherwise, the file simply doesn't exist or an exception occurs.

getAbsolutePath/File(), getCanonicalPath/File() and getPath() Methods

File class has methods that we can use to get a path that represents a file or directory. getAbsoluteFile() and getCanonicalFile() return a File type with pathname in either absolute or canonical format. getAbsolutePath() and getCanonicalPath() return a pathname in String form in either absolute or canonical format. getPath() returns a pathname in String form without any additional formatting. Canonical path is a path which symbols like "." and ".." and other problems like redundant name elements are resolved.
import java.io.File;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args) throws IOException{
    //The pathname here is a relative pathname
    //there should be "folder1" beside the .class
    //file of this source code
    File fileOne = new File("folder1\\testfolder\\..");
    
    if(fileOne.exists()){
      String pathname = fileOne.getAbsolutePath();
      System.out.println("Absolute Path: " + pathname);
      pathname = fileOne.getCanonicalPath();
      System.out.println("Canonical Path: " + pathname);
      pathname = fileOne.getPath();
      System.out.println("Path: " + pathname);
      System.out.println();
      
      System.out.println("get File type with canonical format");
      File fileTwo = fileOne.getCanonicalFile();
      System.out.println("Path: " + fileTwo.getPath());
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}

Result(Yours may be different)
Absolute Path: C:\Users\Admin\Desktop\aaa\SampleClass\folder1\testfolder\..
Canonical Path: C:\Users\Admin\Desktop\aaa\SampleClass\folder1
Path: folder1\testfolder\..

get File type with canonical format
Path: C:\Users\Admin\Desktop\aaa\SampleClass\folder1

getName(), getParent() and getParentFile() Methods

getName() returns the name of a file or directory. getParent() returns a String type pathname of a parent directory of a file or directory. getParentFile() returns a File type of a parent directory.
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists()){
      String fileName = file.getName();
      System.out.println("File Name: " + fileName);
      String parentPath = file.getParent();
      System.out.println("Parent Path: " + parentPath);
      System.out.println();
      
      System.out.println("get parent file");
      File parentFile = file.getParentFile();
      System.out.println("File Name: " + parentFile.getName());
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}

Result
File Name: test.txt
Parent Path: C:\test

get parent file
File Name: test
isDirectory(), isFile() and isHidden() Methods

isDirectory() tests whether a file is a directory or not. isFile() tests whether a file is simply a file or not. isHidden() tests whether a file is hidden or not.
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists()){
      String name = file.getName();
      
      if(file.isFile())
        System.out.println(name + " is a file.");
      
      File parentFile = file.getParentFile();
      name = parentFile.getName();
      
      if(parentFile.isDirectory())
        System.out.println(name + " is a directory.");
        
      if(!parentFile.isHidden())
        System.out.println(name + " is not hidden.");
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}

Result
test.txt is a file.
test is a directory.
test is not hidden.

delete() Method

delete() method deletes the file or directory denoted by this abstract pathname. If this pathname denotes a directory, then the directory must be empty in order to be deleted.
Note: files deleted by the delete() method wouldn't go to the trash/recycle bin. Use moveToTrash() method in Desktop class to move a file to trash/recycle bin.
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists()){
      String name = file.getName();
      if(file.delete())
        System.out.println(name + " is successfully deleted!");
      else System.out.println(name + " couldn't be deleted!");
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}
To delete directory with files, you must loop through the directory and delete all the files in the directory then, you can delete the directory.

renameTo() Method

renameTo() method renames the file denoted by this abstract pathname. Many aspects of the behavior of this method are inherently platform-dependent: The rename operation might not be able to move a file from one filesystem to another, it might not be atomic, and it might not succeed if a file with the destination abstract pathname already exists. The return value should always be checked to make sure that the rename operation was successful.
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    File replacement = new File("C:\\test\\renameTest.txt");
    
    if(file.exists()){
      String name = file.getName();
      if(file.renameTo(replacement))
        System.out.println(name + " is successfully renamed!");
      else System.out.println(name + " couldn't be renamed!");
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}
list() and listFiles() Methods

list() method returns an array of String of names of files and directories in a directory whereas listFiles() returns an array of File of names of files and directories in a directory. list() and listFiles() has other forms but for the sake of simplicity, we're just gonna discuss list() and listFiles().
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test");
    
    if(file.exists()){
      String name = file.getName();
      if(file.isDirectory()){
        System.out.println(name + " is a directory!\n");
        //list() method
        String[] names = file.list();
        
        System.out.println("List Names");
        for(String fileName : names)
          System.out.println(fileName);
          
        System.out.println();
        System.out.println("List Files");
        File[] files = file.listFiles();
        for(File f : files){
          String fileType = f.isFile() ? "is a file."
                            : "is a directory.";
          System.out.println(f.getName() + " " + fileType);
        }
        
      }
      else System.out.println(name + " is not a directory!");
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}

Result(Yours may be different)
List Names
read.txt
renameTest.txt
write.txt

List Files
read.txt is a file.
renameTest.txt is a file.
write.txt is a file.
mkdir() and mkdirs() Methods

mkdir creates a directory and returns true if the operation is successful. mkdirs() creates a directory and other necessary but nonexistent parent directories along the way. return true if the operation is successful. otherwise, returns. Note that if this operation fails it may have succeeded in creating some of the necessary parent directories.
import java.io.File;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\folder1\\folder2");
    
    if(!file.exists()){
      //if folder1 is not existing then mkdir() will fail
      if(file.mkdir()){
        System.out.println(file.getName() + 
                           " is created using mkdir()!");
      }
      else{ 
        System.out.println(file.getName() + 
                           " is not created using mkdir()!");
        //mkdirs() will create folder1 then followed by folder2
        if(file.mkdirs())
          System.out.println(file.getName() + 
                             " is created using mkdirs()!");
        else System.out.println(file.getName() + 
                           " is not created using mkdirs()!");
      }
    }
    else
      System.out.println(file.getName() + " already exists!");
  }
}
toPath() Method

This method converts abstract pathname to a Path type that is associated with the default FileSystem.
import java.io.File;
import java.nio.file.Path;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists()){
      Path path = file.toPath();
      
      System.out.println("Name elements of path: " + 
                          path.toString());
      for(int i = 0; i < path.getNameCount(); i++)
        System.out.println(path.getName(i));
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}
Path Interface

Path interface stores a path of a file. This interface is similar to the The File class. Path interface is used for the features available in java.nio.file package that require a Path interface, whereas File class is used for general-purpose file path storage.

To wrap a file path into a Path object. We can wrap the file path in a File object first then convert the File object to Path object by using toPath() method. Second is to use the get() method in Paths class.
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists()){
      //First method
      //convert File object with pathname to Path object
      Path path = file.toPath();
      //Second method: use the Paths.get() method in the
      //Paths class
      //Path path = Paths.get(file.toString());
      //We can use name elements of a path as arguments
      //in Paths.get()
      //Path path = Paths.get("C:","test","test.txt");
      
      System.out.println("Name elements of path: " + 
                          path.toString());
      for(int i = 0; i < path.getNameCount(); i++)
        System.out.println(path.getName(i));
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}
getFileName() and getName() Methods

getFileName() method returns the file name of a file wrapped in Path object. getName() method returns a name element of a path denoted by an index.
import java.io.File;
import java.nio.file.Path;

public class SampleClass{

  public static void main(String[] args){
    File file = new File("C:\\test\\test.txt");
    
    if(file.exists()){
      //First method
      //convert File object with pathname into Path object
      Path path = file.toPath();
      
      System.out.println(path.getName(0));
      Path path2 = path.getFileName();
      System.out.println(path2.getName(0));
    }
    else
      System.out.println("File Doesn't Exist!");
  }
}

Result
test
test.txt
normalize() Method

normalize() method removes the symbols "." and ".." and redundant name elements in a path. The precise definition of this method is implementation dependent but in general it derives from this path, a path that does not contain redundant name elements. In many file systems, the "." and ".." are special names used to indicate the current directory and parent directory.
import java.io.File;
import java.nio.file.Path;

public class SampleClass{

  public static void main(String[] args){
    //"..\\test" is redundant
    File file = new File("C:\\test\\..\\test\\test.txt");
    //there's ".." 
    File file2 = new File("C:\\test\\..\\test.txt"); 
    File file3 = new File("C:\\test\\..\\test2\\test\\test.txt");
    
    //convert File object with pathname into Path object
    Path path = file.toPath();
    Path path2 = file2.toPath();
    Path path3 = file3.toPath();

    System.out.println(path.normalize());
    System.out.println(path2.normalize());
    System.out.println(path3.normalize());
  }
}

Result
C:\test\test.txt
C:\test.txt
C:\test2\test\test.txt
resolve() Method

resolve() method resolves this path against the given path. This method has a parameter that could be a Path or String type. If the given path is an absolute path then, this method will return the given path as a result. If the given path is empty then, it returns this path. If the given path is not absolute then, this path is considered as a directory and it's resolved against the given path.
import java.nio.file.Path;
import java.nio.file.Paths;

public class SampleClass{

  public static void main(String[] args){
  
    Path path = Paths.get("C:\\test");
    Path otherPath = Paths.get("C:\\test2");
    
    //"this" path is in the "path" variable.
    //given path is the path in the argument
    System.out.println(path.resolve(otherPath));
    
    otherPath = Paths.get("");
    System.out.println(path.resolve(otherPath));
    
    otherPath = Paths.get("test2\\test2.txt");
    System.out.println(path.resolve(otherPath));
  }
}

Result
C:\test2
C:\test
C:\test\test2\test2.txt
resolveSibling() Method

resolveSibling() method resolves the given path against this path's parent path. This has similarities with resolve() method.
import java.nio.file.Path;
import java.nio.file.Paths;

public class SampleClass{

  public static void main(String[] args){
	Path path = Paths.get("C:\\test\\test.txt");
    Path otherPath = Paths.get("myTest.txt");
    
    System.out.println(path.toString());
    //"this" path is in the "path" variable.
    //given path is the path in the argument
    System.out.println(path.resolveSibling(otherPath));
  }
}

Result
C:\test\test.txt
C:\test\myTest.txt
relativize() Method

This method is the inverse of resolution like resolve(). This method attempts to construct a relative path that when resolved against this path, yields a path that locates the same file as the given path.This method creates a relative path when this path is resolved against the given path.

Both paths must have root components if one of them has one and the roots of both paths must be the same. A relative path cannot be constructed if only one of the paths have a root component. It can also be constructed if both paths don't have root component.

Where both paths have a root component then it is implementation dependent if a relative path can be constructed. If this path and the given path are equal then an empty path is returned.
import java.nio.file.Path;
import java.nio.file.Paths;

public class SampleClass{

  public static void main(String[] args){
	Path path = Paths.get("C:\\test\\test1.txt");
    Path otherPath = Paths.get("C:\\myTest\\test2.txt");
    
    //"this" path is in the "path" variable.
    //given path is the path in the argument
    Path relativePath = path.relativize(otherPath);
    System.out.println("relative: "+relativePath);
    System.out.println("resolve: "+path.resolve(relativePath));
    
    path = Paths.get("C:\\test");
    otherPath = Paths.get("C:\\test\\testcase\\myTest1.txt");
    relativePath = path.relativize(otherPath);
    System.out.println("relative: "+relativePath);
    System.out.println("resolve: "+path.resolve(relativePath));
  }
}

Result
relative: ..\..\myTest\test2.txt
resolve: C:\test\test1.txt\..\..\myTest\test2.txt
relative: testcase\myTest1.txt
resolve: C:\test\testcase\myTest1.txt
toFile() Method

This method converts Path type to File type. This method returns a File type that is the converted Path type.
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SampleClass{

  public static void main(String[] args){
    Path path = Paths.get("C:\\test");
    
    File file = path.toFile();
    
    System.out.println(file.getName());
  }
}

Result
test
Files Class

Files class is located at java.nio.file package. This class consists exclusively of static methods that operate on files, directories, or other types of files. We will discuss some methods in this class that may be useful for us.


copy() Method

This method copies a file to a target file. This method has three forms but we're gonna focus on one form:
copy(Path source, Path target, CopyOption... options)
source parameter is the file source, target parameter is the destination and options parameter is a number of options provided by StandardCopyOption and LinkOption. CopyOption is a superinterface of both classes.

You may include any of these options in this method:
REPLACE_EXISTING
COPY_ATTRIBUTES
NOFOLLOW_LINKS

This example demonstrates copy() method.
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

public class SampleClass{

  public static void main(String[] args) throws IOException{
    File source = new File("C:\\test\\test.txt");
    File target = new File("C:\\test\\myTest\\test.txt");
    
    if(!target.exists()){
      Files.copy(source.toPath(), target.toPath());
      System.out.println("Copy Completed!");
    }
    else System.out.println(target.getName() + " already exists!");
    
    
  }
}
In the example above, copy() is used without any CopyOption options. In this invocation, the copy fails(throws exceptions) if the target file already exists or is a symbolic link, except if the source and target are the same file where isSameFile(Path path, Path path2) returns true, in which case the method completes without copying the file.

If we use COPY_ATTRIBUTES but the target file already exists, Files.copy() will throw FileAlreadyExistsException.
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.FileAlreadyExistsException;

public class SampleClass{

  public static void main(String[] args){
    Path source = Paths.get("C:\\test\\test.txt");
    Path target = Paths.get("C:\\test\\myTest\\test.txt");
    
    try{
        //COPY_ATTRIBUTES attempts to copy the source file
        //attributes
    	Files.copy(source, target,
                   StandardCopyOption.COPY_ATTRIBUTES);
        System.out.println("Copy Completed!");
    }
    catch(FileAlreadyExistsException e){
      System.out.println("Couldn't create file. " + 
                         "File already exists");
    }
    catch(IOException e){
      e.printStackTrace();
    }
    
  }
}
If we want to overwrite a target file, we can use the REPLACE_EXISTING option.
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

public class SampleClass{

  public static void main(String[] args) throws IOException{
    Path source = Paths.get("C:\\test\\test.txt");
    Path target = Paths.get("C:\\test\\myTest\\test.txt");
    
    Files.copy(source, target,
               StandardCopyOption.COPY_ATTRIBUTES,
               StandardCopyOption.REPLACE_EXISTING);
    System.out.println("Copy Completed!");
    
  }
}
Note: to copy all files in a non-empty directory, we need to individually copy each of directory's files.

delete() and deleteIfExists() Methods

This methods delete a file. A directory must be empty to be deleted. Otherwise, DirectoryNotEmptyException will be thrown. An implementation may require to examine the file to determine if the file is a directory. Consequently, these methods may not be atomic with respect to other file system operations.
Note: files deleted by these methods wouldn't go to the trash/recycle bin. Use moveToTrash() method in Desktop class to move a file to trash/recycle bin.
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;

public class SampleClass{

  public static void main(String[] args) throws IOException{
    Path path1 = Paths.get("C:\\test\\test.txt");
    Path path2 = Paths.get("C:\\test\\test2.txt");
    
    //This method simply deletes a file
    Files.delete(path1);
    
    //This method deletes a file and returns a
    //boolean value. Returns true if the file
    //is successfully deleted. Returns false
    //if the file didn't exist.
    if(Files.deleteIfExists(path2)){
      System.out.println("File deleted!");
    }
    else System.out.println("File doesn't exist!");
    
  }
}
move() Method

This method moves a file to a target file. failing if the target file exists except if the source and target are the same file, in which case this method has no effect. For a non-empty directory with standard files, moving may involve copying rather than moving directories and this can be done using the copy method in conjunction with the Files.walkFileTree utility method. Thus, Files/directories in the current directory will be moved to the new directory.
We may include these options in this method:
REPLACE_EXISTING
ATOMIC_MOVE
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

public class SampleClass{

  public static void main(String[] args){

    try{
      //in this code, source and target have the same
      //parent directory. In this case, the source will be
      //replaced by the target and test.txt name will be
      //changed to test2.txt
      Path source = Paths.get("C:\\test\\test.txt");
      Path target = source.resolveSibling("test2.txt");
      
      Files.move(source, target, 
                 StandardCopyOption.REPLACE_EXISTING);
      System.out.println("First move operation is a success!");
      
      //In this code, source and target don't have the
      //same parent directory. Thus, testA.txt will simply move
      //to myTest directory, replace an existing testA.txt in
      //myTest directory and testA.txt name will be changed to
      //test.txt
      source = Paths.get("C:\\test\\testA.txt");
      target = Paths.get("C:\\test\\myTest\\test.txt");
      
      Files.move(source, target, 
                 StandardCopyOption.REPLACE_EXISTING);
      System.out.println("Second move operation is a success!");
      
    }
    catch(Exception e){
      e.printStackTrace();
    }
    
  }
}
walkFileTree() Method

This method traverses a file tree. This method has two forms but we're going to focus on this form:
walkFileTree(Path start, FileVisitor<? super Path> visitor)
start parameter is the starting directory and the visitor parameter is a FileVisitor type. We need to create our FileVisitor implementation to manage files in a file tree during the operation. In this tutorial, we're going to create a simple implementation of FileVisitor so, SimpleFileVisitor will suffice.

By default this method form doesn't follow symbolic links. The second form of this method has a parameter that can enable following of symbolic links:
walkFileTree(Path start, Set<FileVisitOption< options, int maxDepth, FileVisitor<? super Path< visitor)
The options parameter determines whether symbolic links should be followed when traversing file tree using this method.

The difference between FileVisitor and SimpleFileVisitor is that SimpleFileVisitor doesn't require all of its methods to be overriden whereas FileVisitor requires all of its methods to be overriden. We also need FileVisitResult 'cause methods of SimpleFileVisitor and FileVisitor require FileVisitResult as return type.

In this tutorial, we're going to do an attempt to delete a non-empty directory. To do that, we need to delete all the files in the directory first before deleting the directory.
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.FileVisitResult;

public class SampleClass{

  public static void main(String[] args) throws IOException{
    Path path = Paths.get("C:\\test\\testfolder");
    
    Files.walkFileTree(path, new MySimpleVisitor());
  }
}

class MySimpleVisitor extends SimpleFileVisitor<Path>{
  
  @Override
  public FileVisitResult visitFile(Path file, 
                         BasicFileAttributes attrs)
                         throws IOException {
    Files.delete(file);
    System.out.println(file.toString() + " file is deleted.");
    return FileVisitResult.CONTINUE;
  }
  
  @Override
  public FileVisitResult postVisitDirectory(Path dir, 
                             IOException e)
                             throws IOException {
                             
    //if e is null it means that the traversing operation
    //is running fine
    if(e == null){
      Files.delete(dir);
      System.out.println(dir.toString() + " directory "+
                         "is deleted.");
    }
    else throw e;
    
    return FileVisitResult.CONTINUE;
  }
}
In the example above, everytime walkFileTree() visits a file, one of the overriden methods in MySimpleVisitor will be called depending on the state of the operation. visitFile() is invoked for a file in a directory. postVisitDirectory() is invoked for a directory after entries in the directory, and all of their descendants, have been visited.

There are two methods that are not overriden here but they can be overriden: preVisitDirectory() and visitFileFailed(). Read the FileVisitor documentation for more details.

No comments:

Post a Comment