JavaRush /Java Blog /Random EN /Java Files, Path

Java Files, Path

Published in the Random EN group
Hello! Today we will talk about working with files and directories. You already know how to manage the contents of files: we had a lot of classes devoted to this :) I think you can easily remember several classes that are needed for these purposes. In today's lecture we will talk specifically about file management - creating, renaming, etc. Before Java 7, all such operations were performed using the File. You can read about his work here . But in Java 7, the creators of the language decided to change how they work with files and directories. This was due to the fact that the class Filehad a number of disadvantages. For example, it didn't have a method copy()that would allow you to copy a file from one location to another (a seemingly clearly needed feature). In addition, the class Filehad quite a lot of methods that returned boolean-values. If an error occurs, such a method returns false rather than throwing an exception, which makes diagnosing errors and determining their causes very difficult. Instead of a single class, Fileas many as 3 classes appeared: Paths, Pathand Files. Well, to be precise, Paththis is an interface, not a class. Let's figure out how they differ from each other and why each of them is needed. Let's start with the easiest thing - Paths.

Paths

Pathsis a very simple class with a single static method get(). It was created solely to obtain an object of type from the passed string or URI Path. It has no other functionality. Here is an example of his work:

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {

   public static void main(String[] args) {

       Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
   }
}
Not the most difficult class, right? :) Well, since we got an object of type Path, let's figure out what it is Pathand why it is needed :)

Path

Path, by and large, is a redesigned analogue of the File. It is much easier to work with than with File. Firstly , many utility (static) methods were removed from it and moved to the class Files. Secondly , Paththe return values ​​of the methods were ordered. In the class, Filethe methods returned this String, that boolean, that File- it was not easy to figure out. For example, there was a method getParent()that returned the parent path for the current file as a string. But at the same time there was a method getParentFile()that returned the same thing, but in the form of an object File! This is clearly redundant. Therefore, in the interface, Paththe method getParent()and other methods for working with files simply return an object Path. No heaps of options - everything is easy and simple. What useful methods does it have Path? Here are some of them and examples of their work:
  • getFileName()— returns the file name from the path;

  • getParent()— returns the “parent” directory in relation to the current path (that is, the directory that is higher in the directory tree);

  • getRoot()— returns the “root” directory; that is, the one that is at the top of the directory tree;

  • startsWith(), endsWith()— check whether the path begins/ends with the passed path:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           Path fileName = testFilePath.getFileName();
           System.out.println(fileName);
    
           Path parent = testFilePath.getParent();
           System.out.println(parent);
    
           Path root = testFilePath.getRoot();
           System.out.println(root);
    
           boolean endWithTxt = testFilePath.endsWith("Desktop\\testFile.txt");
           System.out.println(endWithTxt);
    
           boolean startsWithLalala = testFilePath.startsWith("lalalala");
           System.out.println(startsWithLalala);
       }
    }

    Console output:

    testFile.txt
    C:\Users\Username\Desktop
    C:\
    true
    false

    Pay attention to how the method works endsWith(). It checks if the current path ends with the passed path . It is on the path , and not on the set of characters .

    Compare the results of these two calls:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath.endsWith("estFile.txt"));
           System.out.println(testFilePath.endsWith("Desktop\\testFile.txt"));
       }
    }

    Console output:

    false
    true

    You need to pass the full path to the method endsWith(), and not just a set of characters: otherwise the result will always be false , even if the current path actually ends with such a sequence of characters (as in the case of “estFile.txt” in the example above).

    In addition, there Pathis a group of methods that simplifies working with absolute (full) and relative paths .

Let's look at these methods:
  • boolean isAbsolute()— returns true if the current path is absolute:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath.isAbsolute());
       }
    }

    Console output:

    true

  • Path normalize()— “normalizes” the current path, removing unnecessary elements from it. You may know that popular operating systems often use “.” characters when denoting paths. (“current directory”) and “..” (parent directory). For example: “ ./Pictures/dog.jpg ” means that in the directory we are in now, there is a Pictures folder, and in it there is a file “dog.jpg”

    So here it is. If your program has a path that uses “.” or “..”, the method normalize()will remove them and get a path that will not contain them:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
          
           Path path5 = Paths.get("C:\\Users\\Java\\.\\examples");
          
           System.out.println(path5.normalize());
          
           Path path6 = Paths.get("C:\\Users\\Java\\..\\examples");
           System.out.println(path6.normalize());
       }
    }

    Console output:

    C:\Users\Java\examples
    C:\Users\examples

  • Path relativize()— calculates the relative path between the current and passed path.

    For example:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath1 = Paths.get("C:\\Users\\Users\\Users\\Users");
           Path testFilePath2 = Paths.get("C:\\Users\\Users\\Users\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath1.relativize(testFilePath2));
       }
    }

    Console output:

    Username\Desktop\testFile.txt

The full list of methods Pathis quite large. You can find them all in the Oracle documentation . We'll move on to review Files.

Files

Files- this is a utility class where static methods from the class were moved File. Files- this is approximately the same as Arraysor Collections, only it works with files, and not with arrays and collections :) It is focused on managing files and directories. Using static methods Files, we can create, delete and move files and directories. For these operations the methods are used createFile()(for directories - createDirectory()), move()and delete(). Here's how to use them:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class Main {

   public static void main(String[] args) throws IOException {

       //file creation
       Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
       System.out.println("Was the file created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       // create directory
       Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));

       //move file from desktop to testDirectory. You need to move with the name of the file in the folder!
       testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Has our file been moved to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));

       //remove file
       Files.delete(testFile1);
       System.out.println("Does the file still exist?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
   }
}
Here we first create a file (method Files.createFile()) on the desktop, then create a folder there (method Files.createDirectory()). After that, we move the file (method Files.move()) from the desktop to this new folder, and at the end we delete the file (method Files.delete()). Console output: Was the file created successfully? true Was the directory created successfully? true Is our file still on the desktop? false Has our file been moved to testDirectory? true Does the file still exist? false Pay attention:Just like interface methods Path, many methods Filesreturn an objectPath . Most class methods Filesalso accept Path. Here a method will become your faithful assistant Paths.get()- use it actively. What else is interesting in Files? What the old class really lacked was the ! Filemethod. copy()We talked about him at the beginning of the lecture, now is the time to meet him!

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class Main {

   public static void main(String[] args) throws IOException {

       //file creation
       Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
       System.out.println("Was the file created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       // create directory
       Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));

       //copy the file from the desktop to the directory testDirectory2.
       testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Has our file been copied to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
   }
}
Console output: Was the file created successfully? true Was the directory created successfully? true Is our file still on the desktop? true Was our file copied to testDirectory? true Now you can copy files programmatically! :) But the class Filesallows you not only to manage the files themselves, but also to work with its contents. To write data to a file, it has a method write(), and for reading - as many as 3:, read()and readAllBytes()We readAllLines() will dwell on the latter in detail. Why on it? Because it has a very interesting return type - List<String>! That is, it returns us a list of lines in the file. Of course, this makes working with the content very convenient, because the entire file, line by line, can, for example, be output to the console in a regular loop for:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

   public static void main(String[] args) throws IOException {

       List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);

       for (String s: lines) {
           System.out.println(s);
       }
   }
}
Console output: I remember a wonderful moment: You appeared before me, Like a fleeting vision, Like a genius of pure beauty. Very comfortably! :) This feature appeared in Java 7. In Java 8, Stream API appeared , which added some elements of functional programming to Java. Including richer capabilities for working with files. Imagine that we have a task: find all lines in a file that begin with the word “How”, convert them to UPPER CASE and output them to the console. What would a solution using a class Filesin Java 7 look like? Something like this:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

   public static void main(String[] args) throws IOException {

       List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);

       List<String> result = new ArrayList<>();

       for (String s: lines) {
           if (s.startsWith("How")) {
               String upper = s.toUpperCase();
               result.add(upper);
           }
       }

       for (String s: result) {
           System.out.println(s);
       }
   }
}
Console output: LIKE A FASTING VISION, LIKE A GENIUS OF PURE BEAUTY. We seem to have done it, but don't you think that for such a simple task our code turned out to be a little... verbose? Using the Java 8 Stream API the solution looks much more elegant:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

   public static void main(String[] args) throws IOException {

       Stream<String> stream = Files.lines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"));

       List<String> result  = stream
               .filter(line -> line.startsWith("How"))
               .map(String::toUpperCase)
               .collect(Collectors.toList());
       result.forEach(System.out::println);
   }
}
We achieved the same result, but with much less code! Moreover, it cannot be said that we have lost in “readability”. I think you can easily comment on what this code does, even if you are not familiar with the Stream API. But in short, a Stream is a sequence of elements on which you can perform different functions. We get the Stream object from the method Files.lines()and then apply 3 functions to it:
  1. Using the method, filter()we select only those lines from the file that begin with “How”.

  2. We go through all the selected rows using the method map()and reduce each of them to UPPER CASE.

  3. We combine all the resulting lines into Listusing the collect().

At the exit we get the same result: LIKE A FASTING VISION, LIKE A GENIUS OF PURE BEAUTY. If you are interested in learning more about the capabilities of this library, we recommend reading this article . We will return to our sheep, that is, files :) The last possibility that we will consider today is walking through the file tree . The file structure in modern operating systems most often takes the form of a tree: it has a root and there are branches from which other branches can be separated, etc. Directories play the role of root and branches. For example, the directory “ C:// ” can act as the root . Two branches branch off from it: “ C://Downloads ” and “ C://Users ”. From each of these branches there are 2 more branches: “ C://Downloads/Pictures ”, “ C://Downloads/Video ”, “ C://Users/JohnSmith ”, “ C://Users/Pudge2005 ” . Other branches branch off from these branches, etc. - this is how a tree turns out. In Linux it looks about the same, only there the directory acts as the root / Files, Path - 2 Now imagine that we have a task: knowing the root directory, we must go through it, look into folders of all levels and find files in them with the content we need. We will look for files containing the line “This is the file we need!” Our root directory will be the “testFolder” folder, which is located on the desktop. Inside it has the following contents: Files, Path - 3Inside the folders level1-a and level1-b there are also folders: Files, Path - 4Files, Path - 5Inside these “second-level folders” there are no more folders, only individual files: Files, Path - 6Files, Path - 7We will specially designate 3 files with the content we need with clear names - FileWeNeed1.txt , FileWeNeed2.txt, FileWeNeed3.txt These are the ones we need to find by content using Java. How can we do this? A very powerful method for traversing a file tree comes to the rescue - Files.walkFileTree(). Here's what we need to do. First, we need FileVisitor. FileVisitoris a special interface that describes all the methods for traversing the file tree. Specifically, we'll put logic there to read the contents of the file and check whether it contains the text we need. This is what ours will look like FileVisitor:

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

public class MyFileVisitor extends SimpleFileVisitor<Path> {

   @Override
   public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

       List<String> lines = Files.readAllLines(file);
       for (String s: lines) {
           if (s.contains("This is the file we need")) {
               System.out.println("Required file found!");
               System.out.println(file.toAbsolutePath());
               break;
           }
       }

       return FileVisitResult.CONTINUE;
   }
}
In this case, our class inherits from SimpleFileVisitor. This is a class that implements FileVisitor, in which you need to override only one method: visitFile(). Here we describe what needs to be done with each file in each directory. If you need more complex traversal logic, you should write your own implementation FileVisitor. There you will need to implement 3 more methods:
  • preVisitDirectory()— logic that must be executed before entering the folder;

  • visitFileFailed()— what to do if entering the file is impossible (no access, or other reasons);

  • postVisitDirectory()— the logic that must be executed after entering the folder.

We don’t have such logic, so it’s enough for us SimpleFileVisitor. The logic inside the method visitFile()is quite simple: read all lines from the file, check if they contain the content we need, and if so, print the absolute path to the console. The only line that might give you trouble is this one:

return FileVisitResult.CONTINUE;
In fact, everything is simple. Here we simply describe what the program should do after the file is entered and all the necessary operations are completed. In our case, we need to continue traversing the tree, so we choose the option CONTINUE. But we, for example, could have another task: to find not all files that contain “This is the file we need”, but only one such file . After this, the program must be terminated. In this case, our code would look exactly the same, but instead of break; would:

return FileVisitResult.TERMINATE;
Well, let's run our code and see if it works.

import java.io.IOException;
import java.nio.file.*;

public class Main {

   public static void main(String[] args) throws IOException {

       Files.walkFileTree(Paths.get("C:\\Users\\Username\\Desktop\\testFolder"), new MyFileVisitor());
   }
}
Console output: The required file has been found! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt The required file was found! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt The required file was found! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt Great, we did it! :) If you want to know more about walkFileTree(), I recommend you this article . You can also complete a small task - replace it SimpleFileVisitorwith a regular one FileVisitor, implement all 4 methods and come up with a purpose for this program. For example, you can write a program that will log all your actions: display the name of a file or folder in the console before/after entering them. That's all - see you later! :)
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION