Files

Table of contents

Creating files and folders

To create a file, we use the java.io.File module. First, we have to create an object of the File class, check if a file with a given name already exists, and if not, create it. We can check its properties, like the date of its last modifications, with the self-explanatory methods listed below. To prevent errors, we have to assign throws IOException to the methods in which we create files.


import java.io.File;
import java.io.IOException;
import java.util.Date;

public class Main {
    public static void main(String[] args) throws IOException {
        File file = new File("file.txt");

        if (!file.exists())
            file.createNewFile();

        System.out.println(file.canWrite());
        System.out.println(file.canExecute());
        System.out.println(file.canRead());
        System.out.println(file.isHidden());
        System.out.println(file.isFile()); // if it is a file and not a folder
        System.out.println(new Date(file.lastModified()));
        System.out.println(file.length());

        file.delete();
    }
}
                                    

We create folders using the same class but with the mkdir() method. If we want to make many folders inside each other, we use the mkdirs() method. File.separator substitutes the separator occurring in paths (in Windows it is \). It adjusts automatically to the system it is run on.


File folder = new File("folder");
folder.mkdir();
System.out.println(folder.isDirectory());

File folders = new File("folder2" + File.separator + "folder3");
folders.mkdirs();

System.out.println(System.getProperty("user.dir")); // the path to this file

File file = new File("folder" + File.separator + "file.txt");
if (!file.exists())
    file.createNewFile();
                                    

Paths

The displayPaths() method will list all paths in a given location. It will run through a list of files and folders in the given catalog, ignore the files, and execute itself for each encountered folder separately (so that the folders inside of these folders are also included).


import java.io.File;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        File file = new File("folder" + File.separator + "file.txt");
        if (!file.exists())
            file.createNewFile();
        System.out.println(file.getPath()); // this path is not absolute
        
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalPath()); // it is better because it uses File.separator and not the \ sign

        displayPaths(new File(System.getProperty("user.dir")));
    }

    static void displayPaths(File pathName) {
        String[] fileAndFolderNames = pathName.list();
        System.out.println(pathName.getPath());
        for (int i = 0; i < fileAndFolderNames.length; i++) {
            File f = new File(pathName.getPath(), fileAndFolderNames[i]);
            if (f.isFile())
                System.out.println(f.getPath());
            if (f.isDirectory())
                displayPaths(new File(f.getPath()));
        }
    }
}
                                    

Reading from a text file

To read from a file, we have to create an instance of a BufferedReader class, which uses a FileReader object (with our file's name assigned). The BufferedReader speeds up the whole reading process done by FileReader. When we finish the operations on a file, we have to close it (using the close() method). The loop in the example below will read all of the lines from the file and display them. It will stop when there are no more lines to read.


import java.io.*; // all

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader x = new BufferedReader(new FileReader("file.txt"));

        // System.out.println(reader.read()); - one character
        // System.out.println(reader.readLine()); - one line

        String content = "";
        while ((content = x.readLine()) != null)
            System.out.println(content);
        x.close();
    }
}
                                    

When we don't know the number of arguments we will give to a method, we can use something like in the example below. It may come in handy while reading from a file because we don't know how many lines it has.


static void method(Object... a) {
    for (int i = 0; i < a.length; i++)
        System.out.println(a[i]);
}
                                    

In the example below, it is shown how we can read only one particular line from a file.


int lineNumber = 2;
BufferedReader x = new BufferedReader(new FileReader("file.txt"));
for (int i = 0; i < lineNumber - 1; i++)
    x.readLine();
String line = x.readLine();
System.out.println(line);
                                    

The simplest way to read all lines from a file to a list goes as follows: List lines = Files.readAllLines(Paths.get("file.txt"));.

Writing to a text file

To write to a file, we have to create an instance of a PrintWriter class, which uses a FileWriter object with a file name given. A file can be opened in two modes for saving: append mode, which adds new content at the end of the file, and write mode, which overrides the existing content with new data. If the file doesn’t exist, it creates a new one (the append mode does too). It is specified using the second argument of FileWriter (true - append, false - write). The PrintWriter object simplifies the file writing process and allows for easier handling of formatted output compared to using FileWriter alone. The printf() method is a version of the print() method that supports formatting (println() is also a variation of print() that adds an enter). We can incorporate variables into it through symbols with the % sign. Each symbol represents a specific data type (%d - integer, %.2f - float with two decimal numbers, %s - string), e.g., System.out.printf("%.2f + %.2f", num1, num2);. The variables are substituted in order with the arguments of this method that stand after the main string.


import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        PrintWriter x = new PrintWriter(new FileWriter("file.txt", true));

        x.println(1234);
        x.write("abcd");
        x.println();
        x.printf("He weights %d kg and is %.2f %s tall.", 50, 60.5, "cm");
        x.close();
    }
}
                                    

Tokenization of strings

Tokenization means breaking a string into smaller parts using delimiters (characters treated as separators). It can be used to write a few values in the same line of a file.


import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Example[] example = new Example[3];

        example[0] = new Example();
        example[1] = new Example(0, 10);
        example[2] = new Example(0, 20);

        PrintWriter x = new PrintWriter(new FileWriter("file.txt"));
        Example.writeToFile(example, x);
        x.close();

        BufferedReader y = new BufferedReader(new FileReader("file.txt"));

        Example[] example2 = Example.readFromFile(y);
        for (int i = 0; i < example2.length; i++)
            System.out.println(example2[i]);

        y.close();
    }
}

class Example {
    int x, y;

    Example() {
        this.x = 0;
        this.y = 0;
    }

    Example(int x, int y) {
        this(); // calling the constructor without arguments ("this(x, y)" would call the two-parameter constructor)
        this.x = x;
        this.y = y;
    }

    static void writeToFile(Example[] example, PrintWriter z) {
        z.println(example.length);
        for (int i = 0; i < example.length; i++)
            z.println(example[i].x + "|" + example[i].y);
    }

    static Example[] readFromFile(BufferedReader z) throws IOException {
        int t = Integer.parseInt(z.readLine());
        Example[] example = new Example[t];
        for (int i = 0; i < t; i++) {
            String line = z.readLine();
            StringTokenizer tokens = new StringTokenizer(line, "|");
            int x = Integer.parseInt(tokens.nextToken());
            int y = Integer.parseInt(tokens.nextToken());
            example[i] = new Example(x, y);
        }
        return example;
    }

    public String toString() {return this.x + " " + this.y;}
}
                                    

Full I/O control - pointers

If, instead of the previously mentioned classes, we create an instance of the RandomAccessFile object, we will have more control over what we do, thanks to the pointer inside the file. This pointer is always used to perform operations on the file without our intervention, but by using the seek() method, we can control it directly. In the beginning, as the getFilePointer() method shows, the pointer is located at the end of the file because it just finished writing to it. The number shown by this method is the number of bits the file has (in this case, 24 because there are two double variables and one string with four chars, which gives us 8*2+4*2). The example below uses the class from the previous example.


import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Example[] example = new Example[3];

        example[0] = new Example();
        example[1] = new Example(0, 10);
        example[2] = new Example(0, 20);

        RandomAccessFile raf = new RandomAccessFile("file.txt", "rw"); // r - read, w - write, rw - read & write

        raf.writeDouble(123.45);
        raf.writeDouble(12.34);
        raf.writeChars("ABCD");

        System.out.println(raf.getFilePointer());

        raf.seek(18);
        System.out.println(raf.readChar());

        raf.close();
    }
}
                                    

Serialization

Serialization means converting the state of an object (its data) into a format that can be easily stored and reused. We can serialize an instance of a class. The class that is to be serialized has to implement the Serializable interface. The example below uses the class from the previous example but with added implements Serializable. We can later import the object from the file in another program.


import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Example[] example = new Example[3];

        example[0] = new Example();
        example[1] = new Example(0, 10);
        example[2] = new Example(0, 20);

        ObjectOutputStream x = new ObjectOutputStream(new FileOutputStream("file.txt"));
        x.writeObject(example);
        x.close();

        ObjectInputStream y = new ObjectInputStream(new FileInputStream("file.txt"));
        Example[] z = (Example[])y.readObject();
        for (int i = 0; i < z.length; i++)
            System.out.println(z[i].toString());    
        y.close();
    }
}