IntermediateJava · Lesson 9 of 9

Files & I/O with java.nio

Read and write files with Path and Files, process CSV lines, and walk directories.

The modern file API lives in java.nio.file. A Path represents a location; Files provides operations: writeString, readString, readAllLines, lines, exists, createDirectories, list and walk.

For large files, Files.lines(path) returns a lazy stream of lines — wrap it in try-with-resources so the file is closed. Always specify StandardCharsets.UTF_8 when it matters.

Most file operations throw the checked IOException; handle it where you can show the user a useful message, or declare throws IOException on main in small tools.

FilesDemo.javaJava
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FilesDemo {
    public static void main(String[] args) throws IOException {
        Path dir = Path.of("data");
        Files.createDirectories(dir);

        Path csv = dir.resolve("results.csv");
        Files.writeString(csv, """
            name,subject,score
            Amina,Maths,88
            Amina,Biology,79
            Juma,Maths,42
            Neema,Biology,84
            """, StandardCharsets.UTF_8);

        Map<String, Double> averages;
        try (Stream<String> lines = Files.lines(csv, StandardCharsets.UTF_8)) {
            averages = lines.skip(1)
                .map(line -> line.split(","))
                .collect(Collectors.groupingBy(p -> p[0], TreeMap::new,
                         Collectors.averagingInt(p -> Integer.parseInt(p[2]))));
        }
        System.out.println(averages);

        Path report = dir.resolve("report.txt");
        List<String> reportLines = averages.entrySet().stream()
            .map(e -> "%-8s %5.1f".formatted(e.getKey(), e.getValue()))
            .toList();
        Files.write(report, reportLines, StandardCharsets.UTF_8);
        Files.writeString(report, "Generated by FilesDemo\n", StandardOpenOption.APPEND);

        System.out.print(Files.readString(report));
        System.out.println(Files.size(report) + " bytes, exists=" + Files.exists(report));

        try (Stream<Path> files = Files.list(dir)) {
            System.out.println(files.map(p -> p.getFileName().toString()).sorted().toList());
        }

        try {
            Files.readString(Path.of("missing.txt"));
        } catch (NoSuchFileException e) {
            System.out.println("No such file: " + e.getFile());
        }
    }
}

Key points

  • Use Path + Files from java.nio.file for file work.
  • Close streams from Files.lines/Files.list with try-with-resources.
  • Handle IOException where you can give a helpful message.

Exercise

Write a program that reads every .csv file in a folder (using Files.list and a filter on the file name), combines the results, and writes a summary.txt with each student's average and best subject.

Show solution

Try the exercise yourself first — then compare your approach with this one.

Files.list finds the CSV files (wrapped in try-with-resources), and each file's lines are read and added to a per-student map of subject → score. The summary line for each student shows the average and the best subject, and Files.write saves it.

Summarise.javaJava
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Stream;

public class Summarise {
    public static void main(String[] args) throws IOException {
        Path folder = Files.createDirectories(Path.of("results"));
        Files.writeString(folder.resolve("term1.csv"), "name,subject,score\nAmina,Maths,88\nJuma,Maths,42\n");
        Files.writeString(folder.resolve("term2.csv"), "name,subject,score\nAmina,Biology,79\nJuma,Biology,61\n");

        Map<String, Map<String, Integer>> scores = new TreeMap<>();
        try (Stream<Path> files = Files.list(folder)) {
            for (Path csv : files.filter(p -> p.getFileName().toString().endsWith(".csv")).sorted().toList()) {
                List<String> lines = Files.readAllLines(csv);
                for (String line : lines.subList(1, lines.size())) {     // skip the header
                    String[] p = line.split(",");
                    scores.computeIfAbsent(p[0], k -> new TreeMap<>()).put(p[1], Integer.parseInt(p[2]));
                }
            }
        }

        List<String> summary = new ArrayList<>();
        scores.forEach((student, subjects) -> {
            double avg = subjects.values().stream().mapToInt(Integer::intValue).average().orElse(0);
            String best = Collections.max(subjects.entrySet(), Map.Entry.comparingByValue()).getKey();
            summary.add("%s: average %.1f, best subject %s".formatted(student, avg, best));
        });

        Files.write(Path.of("summary.txt"), summary);
        System.out.print(Files.readString(Path.of("summary.txt")));
        // Amina: average 83.5, best subject Maths
        // Juma: average 51.5, best subject Biology
    }
}

Check your understanding

  1. Which class represents a file location in modern Java?

  2. Why wrap Files.lines(path) in try-with-resources?

  3. Which method reads a small file into a single String?

  4. What exception does Files.readString throw for a missing file?

Ask AI