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. cheeseAppending 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
exitLarge 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: $120Buffered 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 = 4Common 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().