Чтение и запись файлов
Большинство задач с файлами сводится к двум действиям: записать текст и прочитать его обратно. Класс Files делает это одной строкой. Все методы работают в кодировке UTF-8 — она понимает любые языки, включая русский.
Записать и прочитать строку
Files.writeString записывает строку в файл, создавая его при необходимости. Files.readString читает весь файл в одну строку:
void main() throws IOException {
Path path = Path.of("greeting.txt");
Files.writeString(path, "Привет, Анна!");
String text = Files.readString(path);
IO.println(text);
}Результат:
Привет, Анна!Оба метода могут выбросить проверяемое IOException, поэтому у main стоит throws IOException. Если файл уже существовал, writeString полностью заменит его содержимое.
Построчно
Часто файл удобнее обрабатывать по строкам. Files.write записывает список строк, добавляя после каждой перевод строки, а Files.readAllLines возвращает строки списком:
void main() throws IOException {
Path path = Path.of("shopping.txt");
Files.write(path, List.of("молоко", "хлеб", "сыр"));
List<String> lines = Files.readAllLines(path);
IO.println("Строк: " + lines.size());
for (int i = 0; i < lines.size(); i++) {
IO.println((i + 1) + ". " + lines.get(i));
}
}Результат:
Строк: 3
1. молоко
2. хлеб
3. сырДописать в конец
Чтобы добавить текст, не стирая старый, передайте опцию StandardOpenOption.APPEND. Вместе с ней обычно указывают CREATE — создать файл, если его ещё нет:
void main() throws IOException {
Path log = Path.of("app.log");
Files.deleteIfExists(log);
for (String event : List.of("запуск", "вход Олега", "выход")) {
Files.writeString(log, event + "\n", StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
IO.print(Files.readString(log));
}Результат:
запуск
вход Олега
выходБольшие файлы
readAllLines загружает весь файл в память. Для больших файлов лучше Files.lines: он возвращает поток строк и читает файл по мере надобности. Поток держит файл открытым, поэтому его закрывают через try-with-resources:
void main() throws IOException {
Path path = Path.of("sales.csv");
Files.write(path, List.of("Анна,120", "Олег,80", "Ира,200", "Олег,40"));
try (Stream<String> lines = Files.lines(path)) {
int total = lines
.filter(line -> line.startsWith("Олег"))
.mapToInt(line -> Integer.parseInt(line.split(",")[1]))
.sum();
IO.println("Продажи Олега: $" + total);
}
}Результат:
Продажи Олега: $120Буферизованные потоки
Когда текст пишут по частям — например, в цикле, — используют BufferedWriter. Он копит данные в памяти и записывает их на диск крупными порциями, что быстрее. Пару ему составляет 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 + " в квадрате = " + i * i);
writer.newLine();
}
}
try (BufferedReader reader = Files.newBufferedReader(path)) {
IO.println(reader.readLine());
IO.println(reader.readLine());
}
}Результат:
1 в квадрате = 1
2 в квадрате = 4Частая ошибка
Чтение несуществующего файла выбрасывает NoSuchFileException — например, из-за опечатки в имени или запуска программы из другой папки:
void main() throws IOException {
Files.writeString(Path.of("report.txt"), "Отчёт");
IO.println(Files.readString(Path.of("reprot.txt")));
}Результат:
Exception in thread "main" java.nio.file.NoSuchFileException: reprot.txt
at Main.main(Main.java:4)Если файл может отсутствовать, проверьте его через Files.exists или перехватите NoSuchFileException. А чтобы понять, где программа ищет относительный путь, выведите Path.of("").toAbsolutePath().