JavaRush /Курси /JAVA 25 SELF /Читання та запис файлів: базові операції

Читання та запис файлів: базові операції

JAVA 25 SELF
Рівень 35 , Лекція 2
Відкрита

1. Читання файлу цілком

Читання всіх байтів

Іноді потрібно дістати з файлу весь вміст — як є, без розбору: зображення, архів або будь-який двійковий формат. Для цього у Java є метод Files.readAllBytes(path). Він повертає масив байтів (byte[]), тобто сирі дані файлу, які далі можна використовувати як завгодно.

import java.nio.file.*;

public class ReadBytesExample {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        byte[] allBytes = Files.readAllBytes(path);
        System.out.println("Довжина файлу в байтах: " + allBytes.length);
    }
}

Примітка: Якщо файл великий (гігабайти), такий підхід може призвести до проблем із пам’яттю — адже весь вміст файлу завантажується відразу в оперативну пам’ять.

Читання всіх рядків

Для текстових файлів є зручніший спосіб: Files.readAllLines(path). Він повертає список рядків (List<String>), де кожен елемент — окремий рядок з файлу (роздільники рядків ураховуються автоматично).

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

public class ReadLinesExample {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        List<String> lines = Files.readAllLines(path);

        for (String line : lines) {
            System.out.println(line);
        }
    }
}

Важливо: за замовчуванням використовується системне кодування. Якщо хочете вказати його явно (наприклад, UTF-8), скористайтеся перевантаженою версією методу:

List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);

Приклад: читаємо файл і виводимо на екран

Додаймо це до нашого мінізастосунку (наприклад, «блокнот» або «список завдань»):

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

public class TodoReader {
    public static void main(String[] args) throws Exception {
        Path todoPath = Paths.get("todo.txt");
        if (Files.exists(todoPath)) {
            List<String> tasks = Files.readAllLines(todoPath);
            System.out.println("Ваші завдання на сьогодні:");
            for (String task : tasks) {
                System.out.println("- " + task);
            }
        } else {
            System.out.println("Файл todo.txt не знайдено. Створіть список завдань!");
        }
    }
}

2. Запис у файл

Запис масиву байтів

Якщо у вас є масив байтів — наприклад, результат серіалізації, зображення чи аудіо, — використовуйте Files.write(path, bytes). Цей метод створює файл, якщо його немає, або перезаписує наявний.

import java.nio.file.*;

public class WriteBytesExample {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("output.bin");
        byte[] data = {1, 2, 3, 4, 5};
        Files.write(path, data);
        System.out.println("Байти записано до файлу output.bin");
    }
}

Запис списку рядків

Для текстових файлів зручніше використовувати список рядків — кожен рядок буде записано окремо, з переходом на новий рядок.

import java.nio.file.*;
import java.util.Arrays;
import java.util.List;

public class WriteLinesExample {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("todo.txt");
        List<String> tasks = Arrays.asList(
            "Купити молоко",
            "Зателефонувати бабусі",
            "Виконати домашнє завдання з Java"
        );
        Files.write(path, tasks);
        System.out.println("Список завдань записано до todo.txt");
    }
}

Увага: за замовчуванням файл перезаписується. Якщо він уже існує, попередні дані зникнуть. (Як не втратити старі записи — розповімо трохи згодом.)

Запис рядка у файл

Якщо потрібно записати один рядок — просто створіть список з одного рядка або використайте Files.write(path, string.getBytes()):

import java.nio.file.*;

public class WriteStringExample {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("hello.txt");
        String message = "Привіт, Java!";
        Files.write(path, message.getBytes());
        System.out.println("Рядок записано до hello.txt");
    }
}

Явне зазначення кодування

Щоб уникнути «кракозябр» під час роботи з кириличним (і не лише) текстом, завжди вказуйте кодування:

import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;

public class WriteLinesUtf8Example {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("hello.txt");
        List<String> lines = List.of("Привіт, світ!", "Це Java.");
        Files.write(path, lines, StandardCharsets.UTF_8);
    }
}

3. Обробка помилок

Під час роботи з файлами може виникнути виняток IOException. Це загальний сигнал про те, що «щось пішло не так» на рівні введення-виведення. Причин може бути безліч: файлу просто немає, бракує прав доступу, диск переповнений або зайнятий, а інколи навіть витягують флеш-накопичувач у найнедоречніший момент.

Тому методи читання та запису файлів завжди вимагають від нас готовності обробляти такі ситуації. Це робиться через try-catch:

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

public class SafeReadExample {
    public static void main(String[] args) {
        Path path = Paths.get("todo.txt");
        try {
            List<String> lines = Files.readAllLines(path);
            System.out.println("Вміст файлу:");
            for (String line : lines) {
                System.out.println(line);
            }
        } catch (IOException ex) {
            System.out.println("Помилка під час читання файлу: " + ex.getMessage());
        }
    }
}

Навіщо все це?

try-catch перетворює можливий збій програми на контрольовану ситуацію. Якщо файлу немає — ми спокійно повідомимо користувача. Якщо система не надає доступ — вкажемо на проблему. А якщо диск зникне посеред операції, програма не впаде з жахливим трасуванням стека викликів, а щонайменше попередить нормальним повідомленням. Іншими словами: IOException — не ворог, а спосіб, яким Java повідомляє: «Гей, назовні щось пішло не так — вирішуйте, що робити далі».

4. Практичні приклади

Прочитати файл і вивести його вміст

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

public class PrintFileExample {
    public static void main(String[] args) {
        Path path = Paths.get("notes.txt");
        try {
            if (!Files.exists(path)) {
                System.out.println("Файл notes.txt не знайдено.");
                return;
            }
            List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
            System.out.println("Вміст файлу notes.txt:");
            for (String line : lines) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("Помилка під час читання файлу: " + e.getMessage());
        }
    }
}

Записати рядок у новий файл

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

public class WriteFileExample {
    public static void main(String[] args) {
        Path path = Paths.get("greeting.txt");
        String content = "Ласкаво просимо до світу Java I/O!";
        try {
            Files.write(path, content.getBytes(StandardCharsets.UTF_8));
            System.out.println("Рядок записано до файлу greeting.txt");
        } catch (IOException e) {
            System.out.println("Помилка під час запису файлу: " + e.getMessage());
        }
    }
}

Записати список рядків у файл

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

public class WriteListExample {
    public static void main(String[] args) {
        Path path = Paths.get("shopping.txt");
        List<String> items = List.of("Хліб", "Молоко", "Сир");
        try {
            Files.write(path, items, StandardCharsets.UTF_8);
            System.out.println("Список покупок записано до shopping.txt");
        } catch (IOException e) {
            System.out.println("Помилка під час запису файлу: " + e.getMessage());
        }
    }
}

5. Коротко про потоки

Методи Files.readAllBytes, Files.readAllLines, Files.write — це «все й відразу»: або весь файл у пам’ять, або весь вміст відразу на диск. Для невеликих файлів це зручно, а для великих — не дуже (можна отримати OutOfMemoryError або просто «зависнути» на читанні гігабайтних журналів).

Для роботи з великими файлами або для читання рядок за рядком використовують потоки:

  • Для тексту: BufferedReader, BufferedWriter
  • Для байтів: InputStream, OutputStream

Докладніше про потоки ми говоритимемо у наступних лекціях, а поки — невеличкий тизер:

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

public class BufferedReaderExample {
    public static void main(String[] args) {
        Path path = Paths.get("bigfile.txt");
        try (BufferedReader reader = Files.newBufferedReader(path)) {
            String line;
            while ((line = reader.readLine()) != null) {
                // Обробляємо рядок
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("Помилка під час читання файлу: " + e.getMessage());
        }
    }
}

6. Корисні нюанси

  • Перезапис файлу: Усі методи Files.write за замовчуванням перезаписують файл. Якщо потрібно додати в кінець, використовуйте додаткові опції (про це — у наступній лекції).
  • Кодування: Завжди вказуйте кодування явно, якщо працюєте з не-ASCII текстом. Це вбереже від «кракозябр» на виході. Гарний вибір — StandardCharsets.UTF_8.
  • Шляхи: Використовуйте Paths.get(...) для кросплатформності. Ніколи не вписуйте слеші (/ або \) вручну.
  • Імена файлів: Не використовуйте в імені файлу заборонені символи (?, *, :, <, >, | тощо), особливо якщо ваш код має працювати у Windows.

7. Типові помилки під час читання та запису файлів

Помилка № 1: Не обробляється IOException. Дуже часто новачки пишуть Files.readAllLines(path) без try-catch, і за першої ж проблеми (файл не знайдено, немає прав, пошкоджено диск) програма падає. Завжди обробляйте винятки!

Помилка № 2: Робота з великими файлами через readAllBytes/readAllLines. Якщо файл важить сотні мегабайт або гігабайти, спроба завантажити його цілком може «завалити» вашу програму. Для таких випадків використовуйте потоки (BufferedReader).

Помилка № 3: Не вказано кодування під час запису/читання тексту. Якщо не вказати кодування, на різних комп’ютерах і ОС результат може відрізнятися. Особливо часто проблема проявляється з кирилицею. Використовуйте StandardCharsets.UTF_8 або потрібне вам кодування явно.

Помилка № 4: Очікування, що File/Path — це сам файл. Клас File або Path — це лише «ярлик» на файл, а не сам файл. Створивши об’єкт, ви не створюєте файл на диску. Щоб створити файл — використовуйте методи Files.createFile, Files.write тощо.

Помилка № 5: Потік не закривається (якщо використовуєте потоки). Якщо ви використовуєте потоки вручну (наприклад, BufferedReader), обов’язково закривайте їх (краще через try-with-resources). Інакше файл може залишитися заблокованим і недоступним для інших програм.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ