JavaRush /Курси /JAVA 25 SELF /Задання кодування під час читання й запису файлів

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

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

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
StandardCharsets.UTF_8
UTF-16
StandardCharsets.UTF_16
ISO-8859-1
StandardCharsets.ISO_8859_1
Windows-1251
Charset.forName("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 або явно не закрити потік, частина даних може не потрапити на диск.

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