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). Інакше файл може залишитися заблокованим і недоступним для інших програм.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ