JavaRush /Курси /JAVA 25 SELF /Проблеми невідповідності кодувань, типові помилки

Проблеми невідповідності кодувань, типові помилки

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

1. Симптоми помилок

В ідеальному світі програмісти завжди знають, у якому кодуванні збережено файл, і правильно зазначають його під час читання. Але реальність — це світ, де файли мандрують між Windows, Linux, серверами, редакторами, і кожен по‑своєму трактує байти. У результаті ми стикаємося з такими симптомами:

  • «Кракозябри» — замість очікуваного тексту бачимо дивні символи, знаки питання, квадратики або набір літер, який не схожий на жодну мову світу.
  • Втрата символів — частина тексту зникає або замінюється на ?.
  • Винятки — наприклад, MalformedInputException, коли Java не може «перетравити» байти у вибраному кодуванні.
  • Помилки під час парсингу — програма не може правильно обробити файл, тому що ключові слова або структури пошкоджені через спотворення тексту.

Ось класичний приклад «кракозябр» під час читання кириличного файлу в неправильному кодуванні:

Очікували:  Привіт, світе!
Отримали: Привет, мир

Це не нова мова, а результат того, що байти були інтерпретовані не тим «словником», який потрібен.

2. Чому виникають помилки: корінь зла

Файл збережено в одному кодуванні, а читається в іншому

Припустімо, хтось зберіг файл у Windows-1251, а ви відкриваєте його в UTF-8. Java чесно намагається декодувати байти за правилами UTF-8, та виходить нісенітниця, тому що значення байтів не збігаються з очікуваними.

Використання системного кодування «за замовчуванням»

Якщо ви не зазначаєте кодування явно, Java використовує системне — те, що встановлене на вашому комп’ютері. У Windows із російською локаллю це може бути Windows-1251, у Linux — UTF-8, на Mac — теж UTF-8. Файл, який чудово відкривається у вас, може стати непридатним для читання у колеги з іншою ОС.

Використання застарілих конструкторів

У старих версіях Java (і в деяких підручниках) часто трапляються такі конструкції з FileReader/FileWriter, які використовують системне кодування і не дають вам контролю — це пастка та джерело «кракозябр».

FileReader reader = new FileReader("file.txt");
FileWriter writer = new FileWriter("file.txt");

Наявність або відсутність BOM (Byte Order Mark)

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

3. Як проявляються помилки: розбір на практиці

Приклад 1: Файл із кирилицею, збережений у Windows-1251, читається як UTF-8

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

public class EncodingDemo {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("russian.txt");
        // Файл збережено у Windows-1251, читаємо як UTF-8 — будуть кракозябри!
        try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            System.out.println(reader.readLine());
        }
    }
}

У результаті замість «Привіт, світе!» ви побачите набір дивних символів.

Приклад 2: Файл збережено в UTF-8, читається як ISO-8859-1

try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.ISO_8859_1)) {
    System.out.println(reader.readLine());
}

Результат: Усі не-ASCII-символи будуть перетворені на сміття або замінені на ?.

Приклад 3: Виняток під час читання файлу

Якщо байти не відповідають правилам обраного кодування, Java може кинути виняток:

Exception in thread "main" java.nio.charset.MalformedInputException: Input length = 1
    at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    ...

Це означає, що Java зустріла байт, який не може бути коректно інтерпретований у вибраному кодуванні.

4. Діагностика: як зрозуміти, що не так із кодуванням

Перевіряйте кодування файлу

  • У редакторах на кшталт Notepad++, VS Code або Sublime Text зазвичай можна переглянути або змінити кодування файлу (зазвичай на нижній панелі).
  • У Linux командою можна отримати підказку про кодування (але не завжди на 100 % точно):
file имя_файла.txt

Перевіряйте системне кодування Java

Виведіть у консоль значення властивості file.encoding:

System.out.println(System.getProperty("file.encoding"));

Використовуйте тестові дані

Створіть невеликий файл із різними символами (кириличні, латинські, спецсимволи, емодзі), спробуйте прочитати його з різними кодуваннями і подивіться, коли результат збігається з очікуваннями.

Завжди явно вказуйте кодування

Щойно ви бачите в коді читання/запис файлу без зазначення кодування — це привід насторожитися. Наприклад, використовуйте Files.newBufferedReader(..., StandardCharsets.UTF_8) замість «за замовчуванням».

5. Найкращі практики: як не потрапити в пастку

Правило № 1:
ЗАВЖДИ явно вказуйте кодування під час роботи з файлами, особливо якщо файл буде використовуватися на різних комп’ютерах, у різних ОС або надсилатися мережею.

Правило № 2:
Використовуйте сучасні, універсальні кодування — насамперед UTF-8 (StandardCharsets.UTF_8). Лише якщо є особливі вимоги (наприклад, інтеграція зі старою системою), використовуйте інші кодування.

Правило № 3:
Уникайте класів FileReader і FileWriter (вони не дозволяють вказати кодування), замість них використовуйте InputStreamReader, OutputStreamWriter або методи з Files із явним Charset.

Правило № 4:
Перевіряйте результат! Відкривайте записані файли в редакторах із підтримкою різних кодувань, щоб упевнитися, що текст виглядає коректно.

6. Особливості й нюанси: BOM, XML, JSON та інші «веселі» випадки

BOM (Byte Order Mark): іноді файл у UTF-8 починається з «невидимих» байтів (EF BB BF). Більшість сучасних програм їх ігнорує, але деякі можуть показати «кракозябру» на початку рядка або не прийняти файл (наприклад, старі парсери XML/JSON).

XML/HTML: інколи на початку файлу є рядок на кшталт <?xml version="1.0" encoding="UTF-8"?>. Він повідомляє програмі, у якому кодуванні очікувати байти. Але якщо фактичне кодування не збігається з оголошеним — знову «кракозябри».

JSON: за стандартом має бути у UTF-8, але якщо файл створено у Windows-1251, парсер видасть помилку або спотворені дані.

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