JavaRush /Курсы /JAVA 25 SELF /Режимы работы с файлами: чтение, запись, дозапись

Режимы работы с файлами: чтение, запись, дозапись

JAVA 25 SELF
35 уровень , 3 лекция
Открыта

1. Запись с перезаписью

Когда вы работаете с файлами, важно понимать, что «записать в файл» — это не всегда одно и то же. Иногда нужно полностью перезаписать файл (например, когда вы сохраняете новый отчёт), а иногда — добавить новую информацию в конец уже существующего файла (например, вести лог событий приложения). В некоторых случаях нужно просто прочитать содержимое файла, не меняя его.

В Java (особенно в современном пакете java.nio.file) режим работы с файлом определяется набором специальных опций, которые вы передаёте в методы записи, например, в Files.write(). Эти опции позволяют явно указать, хотите ли вы перезаписать файл или добавить что-то в конец.

Как это работает

Когда вы вызываете Files.write(path, data), Java по умолчанию создаёт новый файл или перезаписывает уже существующий. Всё старое содержимое файла будет уничтожено, и на его месте окажутся только новые данные.

Это поведение по умолчанию — своего рода «жёсткая перезагрузка» файла. Если в файле раньше было 1000 строк, а вы записали одну — всё, что было раньше, исчезнет без следа.

Пример: запись строки в файл

import java.nio.file.*;
import java.io.IOException;
import java.util.List;

public class OverwriteFileExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("myfile.txt");
        List<String> lines = List.of("Привет, мир!", "Это новая запись в файле.");

        // Записываем строки в файл (старое содержимое будет удалено)
        Files.write(path, lines);

        System.out.println("Файл успешно перезаписан!");
    }
}

После запуска этого кода в файле myfile.txt останутся только две строки из списка lines. Всё, что было раньше, исчезнет (без всяких «сожалений» со стороны Java).

2. Дозапись (append): добавление данных в конец файла

В некоторых задачах (например, ведение журнала событий, логирование, накопление данных) требуется не уничтожать старое содержимое файла, а добавлять новые строки в конец. В старом API для этого часто использовали FileWriter с флагом append = true. В современном API всё делается проще и явно.

Как это делается

Всё, что нужно — передать опцию StandardOpenOption.APPEND в метод Files.write():

import java.nio.file.*;
import java.io.IOException;
import java.util.List;

public class AppendFileExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("myfile.txt");
        List<String> moreLines = List.of("Добавили ещё одну строку.", "И ещё одну!");

        // Добавляем строки в конец файла (старое содержимое сохраняется)
        Files.write(path, moreLines, StandardOpenOption.APPEND);

        System.out.println("Строки добавлены в конец файла!");
    }
}

Важный момент: если файл не существует, при попытке дозаписи возникнет ошибка — Java не будет создавать файл автоматически в режиме APPEND. Чтобы создать файл, если его нет, используйте сразу две опции:

Files.write(path, moreLines, StandardOpenOption.APPEND, StandardOpenOption.CREATE);

Как это выглядит на практике

  • Вы запускаете программу с записью (перезаписью) — в файле две строки.
  • Запускаете программу с дозаписью — к этим строкам добавляются новые, старые остаются на месте.

3. Комбинирование опций: CREATE, APPEND, TRUNCATE_EXISTING

В Java можно комбинировать несколько опций для более гибкого управления режимами открытия файла:

  • StandardOpenOption.CREATE — создать файл, если его нет.
  • StandardOpenOption.APPEND — добавлять данные в конец.
  • StandardOpenOption.TRUNCATE_EXISTING — обрезать файл до нуля байт (очистить), если он существует.
  • StandardOpenOption.CREATE_NEW — создать новый файл; если уже существует — ошибка.

Пример: создать файл, если его нет, и добавить данные в конец

Files.write(path, moreLines, StandardOpenOption.CREATE, StandardOpenOption.APPEND);

Пример: создать файл или полностью очистить и записать новые данные

Files.write(path, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

Таблица: основные режимы записи

Опция(и) Поведение
(по умолчанию) Создать файл или перезаписать существующий
APPEND
Добавить в конец, ошибка если файла нет
APPEND + CREATE
Добавить в конец, создать файл если его нет
TRUNCATE_EXISTING
Обрезать файл до нуля и записать новые данные
CREATE_NEW
Создать новый файл, ошибка если файл уже существует

4. Чтение и запись бинарных файлов

До сих пор мы работали со строками. Но иногда нужно записывать или читать «сырые» байты (например, изображения, архивы, PDF-файлы). В этом случае используйте:

  • Files.readAllBytes(path) — читает файл в массив байт.
  • Files.write(path, byteArray) — записывает массив байт в файл.

Пример: копирование файла

import java.nio.file.*;
import java.io.IOException;

public class CopyBinaryFileExample {
    public static void main(String[] args) throws IOException {
        Path source = Paths.get("logo.png");
        Path target = Paths.get("logo_copy.png");

        byte[] data = Files.readAllBytes(source);
        Files.write(target, data);

        System.out.println("Файл скопирован!");
    }
}

Пример: дозапись бинарных данных

byte[] moreData = new byte[] {1, 2, 3, 4, 5};
Files.write(path, moreData, StandardOpenOption.APPEND, StandardOpenOption.CREATE);

Внимание: дозапись бинарных данных в файл, который уже содержит структурированные данные (например, картинку), обычно приведёт к его порче. Используйте дозапись только для текстовых или специально подготовленных бинарных файлов (например, логов).

5. Когда нужны потоки (FileInputStream, BufferedReader и др.)

Методы Files.readAllBytes(), Files.write() удобны для небольших и средних файлов, когда можно спокойно загрузить всё содержимое в память за один раз. Если файл большой (например, гигабайты), или если вы хотите читать его построчно (например, для анализа логов), используйте потоки.

Пример: чтение файла построчно с помощью BufferedReader

import java.nio.file.*;
import java.io.*;

public class ReadLinesExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("myfile.txt");

        try (BufferedReader reader = Files.newBufferedReader(path)) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println("Строка: " + line);
            }
        }
    }
}

Пример: запись файла построчно с помощью BufferedWriter и дозаписью

import java.nio.file.*;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class WriteAppendExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("myfile.txt");

        try (BufferedWriter writer = Files.newBufferedWriter(
                path,
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND)) {
            writer.write("Ещё одна строка через BufferedWriter!");
            writer.newLine();
        }
    }
}

6. Практика: создаём файл, дозаписываем в него строки

Давайте свяжем всё в единое приложение. Пусть у нас есть программа, которая ведёт список задач (todo-list) в текстовом файле. Каждый раз, когда пользователь добавляет новую задачу, мы дозаписываем её в конец файла.

import java.nio.file.*;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;

public class TodoList {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("todo.txt");
        Scanner scanner = new Scanner(System.in);

        System.out.print("Введите новую задачу: ");
        String task = scanner.nextLine();

        // Дозаписываем задачу в конец файла (создаём файл, если его нет)
        Files.write(path, 
            List.of(task), 
            StandardOpenOption.CREATE, 
            StandardOpenOption.APPEND);

        System.out.println("Задача добавлена!");
    }
}

Запустите программу несколько раз — каждая задача появится в файле на новой строке. Вот и получился наш первый «вечный» todo-лист!

7. Обработка ошибок: что может пойти не так?

Работа с файлами всегда связана с риском ошибок. Вот что может случиться:

  • IOException — базовое исключение для всех ошибок ввода-вывода. Может возникнуть, если файл занят другой программой, если нет прав на запись/чтение, если диск переполнен и т.д.
  • NoSuchFileException — если вы пытаетесь прочитать или дозаписать в файл, который не существует (и не указали опцию CREATE).
  • FileAlreadyExistsException — если вы используете опцию CREATE_NEW, а файл уже есть.

Рекомендация: всегда оборачивайте работу с файлами в try-catch:

try {
    // ваши операции с файлами
} catch (IOException e) {
    System.out.println("Ошибка при работе с файлом: " + e.getMessage());
}

8. Типичные ошибки при работе с режимами открытия файлов

Ошибка №1: забыли указать опцию CREATE при дозаписи. Если вы пытаетесь дозаписать (APPEND) в файл, которого ещё нет, получите NoSuchFileException. Всегда добавляйте CREATE, если хотите, чтобы файл создавался автоматически.

Ошибка №2: случайная перезапись файла. Вызов Files.write(path, data) без дополнительных опций уничтожает всё старое содержимое файла. Если хотите добавить данные, а не уничтожить старые, используйте APPEND.

Ошибка №3: попытка дозаписи бинарных данных в файл с текстом (или наоборот). Если вы смешиваете текстовые и бинарные данные в одном файле, скорее всего, получите нечитаемый файл. Всегда придерживайтесь одного формата для одного файла.

Ошибка №4: забыли закрыть поток. Если используете потоки (BufferedWriter, BufferedReader), не забывайте закрывать их (лучше через try-with-resources), иначе файл может остаться заблокированным.

Ошибка №5: не обработали исключения. Любая операция с файлами может выбросить IOException. Если не обработать ошибку, программа завершится аварийно.

1
Задача
JAVA 25 SELF, 35 уровень, 3 лекция
Недоступна
Записываем новые события в журнал без потерь 📝
Записываем новые события в журнал без потерь 📝
1
Задача
JAVA 25 SELF, 35 уровень, 3 лекция
Недоступна
Умный конфигуратор для вашего приложения ⚙️
Умный конфигуратор для вашего приложения ⚙️
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ