Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Reading and writing files

Most file tasks come down to two actions: write some text and read it back. The Files class does this in one line. All its methods use the UTF-8 encoding, which handles any language.

Writing and reading a string

Files.writeString writes a string to a file, creating the file if needed. Files.readString reads the whole file into one string:

void main() throws IOException {
    Path path = Path.of("greeting.txt");
 
    Files.writeString(path, "Hello, Anna!");
    String text = Files.readString(path);
 
    IO.println(text);
}

Output:

Hello, Anna!

Both methods may throw the checked IOException, so main declares throws IOException. If the file already existed, writeString replaces its contents completely.

Line by line

A file is often easier to process line by line. Files.write writes a list of strings, adding a line break after each, and Files.readAllLines returns the lines as a list:

void main() throws IOException {
    Path path = Path.of("shopping.txt");
    Files.write(path, List.of("milk", "bread", "cheese"));
 
    List<String> lines = Files.readAllLines(path);
    IO.println("Lines: " + lines.size());
    for (int i = 0; i < lines.size(); i++) {
        IO.println((i + 1) + ". " + lines.get(i));
    }
}

Output:

Lines: 3
1. milk
2. bread
3. cheese

Appending to the end

To add text without erasing the old one, pass the StandardOpenOption.APPEND option. It usually goes together with CREATE — create the file if it doesn't exist yet:

void main() throws IOException {
    Path log = Path.of("app.log");
    Files.deleteIfExists(log);
 
    for (String event : List.of("start", "Oleg logged in", "exit")) {
        Files.writeString(log, event + "\n", StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
 
    IO.print(Files.readString(log));
}

Output:

start
Oleg logged in
exit

Large files

readAllLines loads the whole file into memory. For large files Files.lines is better: it returns a stream of lines and reads the file as needed. The stream keeps the file open, so it is closed with try-with-resources:

void main() throws IOException {
    Path path = Path.of("sales.csv");
    Files.write(path, List.of("Anna,120", "Oleg,80", "Ira,200", "Oleg,40"));
 
    try (Stream<String> lines = Files.lines(path)) {
        int total = lines
                .filter(line -> line.startsWith("Oleg"))
                .mapToInt(line -> Integer.parseInt(line.split(",")[1]))
                .sum();
        IO.println("Oleg's sales: $" + total);
    }
}

Output:

Oleg's sales: $120

Buffered streams

When text is written in pieces — say, in a loop — a BufferedWriter is used. It collects data in memory and writes it to disk in large chunks, which is faster. Its partner is the BufferedReader:

void main() throws IOException {
    Path path = Path.of("squares.txt");
 
    try (BufferedWriter writer = Files.newBufferedWriter(path)) {
        for (int i = 1; i <= 3; i++) {
            writer.write(i + " squared = " + i * i);
            writer.newLine();
        }
    }
 
    try (BufferedReader reader = Files.newBufferedReader(path)) {
        IO.println(reader.readLine());
        IO.println(reader.readLine());
    }
}

Output:

1 squared = 1
2 squared = 4

Common mistake

Reading a file that doesn't exist throws NoSuchFileException — for example, because of a typo in the name or because the program was started from another folder:

void main() throws IOException {
    Files.writeString(Path.of("report.txt"), "Report");
 
    IO.println(Files.readString(Path.of("reprot.txt")));
}

Output:

Exception in thread "main" java.nio.file.NoSuchFileException: reprot.txt
	at Main.main(Main.java:4)

If the file may be missing, check it with Files.exists or catch NoSuchFileException. And to see where the program looks for a relative path, print Path.of("").toAbsolutePath().

Thanks for reading!

Donate to the site — we work hard for you.