1. Вступ
Домовімося відверто: якщо ви бодай раз бачили замість кириличного тексту щось на кшталт Привет, ви вже стали жертвою неправильного кодування. Це трапляється, коли файл було записано в одному кодуванні, а читається в іншому. Наприклад, файл збережено як UTF-8, а читається як Windows-1251, або навпаки.
Java за замовчуванням використовує системне кодування, яке можна дізнатися так:
System.out.println(System.getProperty("file.encoding"));
На одному комп’ютері це може бути UTF-8, на іншому — Windows-1251, а десь ще — ISO-8859-1. Саме тому краще завжди вказувати кодування явно. Це особливо важливо, якщо доводиться працювати з багатомовними даними, якщо файли використовуватимуться на різних комп’ютерах або відкриватимуться в інших програмах, або якщо потрібно, щоб ваш код поводився однаково в будь-якому середовищі, а не лише на вашій машині.
Клас Charset: ваш друг у світі кодувань
У Java для роботи з кодуваннями використовується клас java.nio.charset.Charset. Він дає змогу задавати кодування як за назвою (наприклад, "UTF-8"), так і використовувати стандартні константи (StandardCharsets.UTF_8).
Приклади стандартних кодувань:
| Кодування | Константа Java |
|---|---|
| UTF-8 | |
| UTF-16 | |
| ISO-8859-1 | |
| Windows-1251 | |
Використання констант має переваги: менше шансів помилитися в назві, і не виникне UnsupportedCharsetException.
2. Читання файлів із зазначенням кодування
Старий спосіб:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("example.txt"),
StandardCharsets.UTF_8 // <-- Явно вказуємо кодування
)
);
Сучасний спосіб:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;
BufferedReader reader = Files.newBufferedReader(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- Явно вказуємо кодування
);
Рекомендується використовувати другий спосіб — він коротший, безпечніший і зручно поєднується з try-with-resources.
Приклад: читаємо рядок із файлу
try (BufferedReader reader = Files.newBufferedReader(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
String line = reader.readLine();
System.out.println("Зчитано: " + line);
}
Чому це важливо: Якщо файл було збережено в UTF-8, а ви читаєте його як Windows-1251, кириличні символи спотворяться. Якщо вказати правильне кодування, текст читатиметься коректно на будь-якій ОС.
3. Запис файлів із зазначенням кодування
Старий спосіб:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("example.txt"),
StandardCharsets.UTF_8 // <-- Явно вказуємо кодування
)
);
Сучасний спосіб:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedWriter;
BufferedWriter writer = Files.newBufferedWriter(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- Явно вказуємо кодування
);
Приклад: пишемо рядок у файл
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
writer.write("Привіт, світ!");
}
Результат: Файл буде збережено в UTF-8, і його можна буде коректно відкрити в будь-якому редакторі, що підтримує UTF-8.
4. Корисні нюанси
Як дізнатися підтримувані кодування
import java.nio.charset.Charset;
public class ListCharsets {
public static void main(String[] args) {
System.out.println("Доступні кодування:");
Charset.availableCharsets().forEach((name, charset) -> System.out.println(name));
}
}
Порада: Якщо ви використовуєте екзотичне кодування (наприклад, для давньокитайських ієрогліфів або марсіанських смайликів), перевірте, чи підтримується воно вашою JVM.
Використання try-with-resources: не забуваємо закривати потоки
Працюючи з файлами, важливо закривати потоки, щоб не було витоків ресурсів. Сучасний Java-код використовує конструкцію try-with-resources:
try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
// Працюємо з файлом
}
Потік закриється автоматично, навіть якщо станеться помилка.
Рекомендації
- Краще завжди явно вказувати кодування під час читання й запису файлів, навіть якщо ви впевнені, що «за замовчуванням усе працює».
- Використовуйте UTF-8 для нових файлів — це де-факто стандарт, особливо якщо ви працюєте з вебом, JSON, XML, чи хочете, щоб ваші файли були читабельними всюди.
- Для старих файлів (наприклад, вивантаження з 1С, старих баз даних, CSV з Windows) використовуйте те кодування, у якому їх було збережено (наприклад, Windows-1251, ISO-8859-1).
- Не використовуйте застарілі класи, де кодування не вказується явно: FileReader/FileWriter. Натомість застосовуйте InputStreamReader/OutputStreamWriter з явним кодуванням або методи з Files.
- Для великих файлів використовуйте буферизацію (BufferedReader/BufferedWriter,) щоб не «з’їсти» всю пам’ять.
5. Типові помилки під час роботи з кодуваннями
Помилка № 1: не вказано кодування під час читання/запису файлу.
Якщо не вказати кодування, Java використовує системне за замовчуванням ("file.encoding"). На вашій машині все працює, а в колеги — «кракозябри».
Помилка № 2: невідповідність кодувань під час читання і запису.
Файл було записано в одному кодуванні, а читається в іншому. Наприклад, файл записано в UTF-8, а читається як Windows-1251 — кирилиця спотворюється.
Помилка № 3: використання застарілих класів FileReader/FileWriter.
Ці класи не дають змоги явно вказати кодування — використовувати їх не рекомендується. Замість них використовуйте InputStreamReader/OutputStreamWriter із зазначенням кодування або методи з Files.
Помилка № 4: помилка в назві кодування.
Наприклад, написали "utf8" замість "UTF-8" або "win1251" замість "Windows-1251". Java викине виняток UnsupportedCharsetException.
Помилка № 5: потік не закрито — файл не записався.
Якщо не використовувати try-with-resources або явно не закрити потік, частина даних може не потрапити на диск.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ