JavaRush /Курси /JAVA 25 SELF /Основні кодування: UTF-8, UTF-16, ISO-8859-1

Основні кодування: UTF-8, UTF-16, ISO-8859-1

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

1. UTF-8 — король сучасних кодувань

У роботі програміста ви зіткнетеся з файлами, мережевими протоколами, базами даних, веб‑сервісами, де використовуються різні кодування. Якщо ви не знаєте, чим UTF-8 відрізняється від ISO-8859-1, то легко потрапите в пастку: ваш код працюватиме «у мене на комп’ютері», але зламається у колеги з Франції або в хмарі, де інше кодування за замовчуванням.

Сьогодні розберемо три основні кодування, які трапляються в реальному житті Java‑розробника:

  • UTF-8 — сучасний універсальний стандарт.
  • UTF-16 — внутрішній формат рядків у Java і часто трапляється у Windows.
  • ISO-8859-1 (Latin-1) — старе, але досі уживане однобайтне кодування для західноєвропейських мов.

Познайомимося з ними ближче!

Що це таке?

UTF-8 (Unicode Transformation Format, 8-bit) — це кодування, яке вміє представляти будь-який символ із величезної таблиці Unicode (а це і кирилиця, і китайські ієрогліфи, і емодзі, і навіть рідкісні стародавні писемності). Причому робить це дуже хитро: для найпопулярніших символів (англійські літери, цифри, розділові знаки) використовується лише 1 байт. Для інших — 2, 3 або навіть 4 байти.

Факт: Усі сучасні сайти, більшість JSON- і XML-файлів і навіть вихідні файли Java за замовчуванням використовують UTF-8.

Скільки потрібно байтів для одного символу?

  • Символи ASCII (англійські літери, цифри, розділові знаки) — 1 байт.
  • Кириличні символи, європейські символи з діакритикою2 байти.
  • Китайські, японські, корейські ієрогліфи3 байти.
  • Суперекзотика, емодзі4 байти.

Приклад:

Символ Код у Unicode UTF-8 (у байтах)
A
U+0041
41
Я
U+042F
D0 AF
U+20AC
E2 82 AC
😀
U+1F600
F0 9F 98 80

Чому UTF-8 — це круто?

  • Зворотно сумісний з ASCII: якщо файл містить лише англійські символи, він виглядає абсолютно так само, як у ASCII.
  • Компактність: для коротких текстів англійською мовою файл у UTF-8 буде мінімального розміру.
  • Міжнародність: підтримує всі мови світу, включно з емодзі.
  • Стандарт для вебу: HTML, CSS, JSON, XML, JavaScript — усе це за замовчуванням UTF-8.

Приклад використання в Java

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

public class Utf8Demo {
    public static void main(String[] args) throws Exception {
        // Запис рядка у файл у кодуванні UTF-8
        String text = "Привіт, світ! 😀";
        Files.write(Paths.get("utf8.txt"), text.getBytes(StandardCharsets.UTF_8));

        // Читання файлу в UTF-8
        String read = Files.readString(Paths.get("utf8.txt"), StandardCharsets.UTF_8);
        System.out.println(read); // Виведе: Привіт, світ! 😀
    }
}

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

Коли використовувати UTF-8?

Завжди, якщо тільки у вас немає дуже вагомої причини обрати щось інше. Це універсальний вибір для всіх сучасних застосунків.

2. UTF-16 — внутрішній формат Java

UTF-16 — це ще одне кодування з сімейства Unicode. На відміну від UTF-8, воно найчастіше використовує 2 байти на символ. Саме так Java зберігає рядки (String) і символи (char) всередині JVM. Але є нюанс: деякі символи (наприклад, рідкісні ієрогліфи або емодзі) потребують уже 4 байти (сурогатна пара).

Де використовується UTF-16?

  • Усередині Java: усі рядки та символи — це UTF-16.
  • Windows: багато системних файлів у Windows використовують UTF-16 (наприклад, Notepad за замовчуванням зберігає в UTF-16 LE).
  • Деякі мережеві протоколи й формати: наприклад, XML може бути в UTF-16.

Приклад: як виглядає рядок у UTF-16

Символ Код у Unicode UTF-16 (байти) Коментар
A
U+0041
00 41
2 байти
Я
U+042F
04 2F
2 байти
U+20AC
20 AC
2 байти
😀
U+1F600
D8 3D DE 00
4 байти (сурогатна пара)

Приклад використання в Java

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

public class Utf16Demo {
    public static void main(String[] args) throws Exception {
        String text = "Привіт, світ! 😀";

        // Запис рядка у файл із використанням кодування UTF-16
        Files.write(Paths.get("utf16.txt"), text.getBytes(StandardCharsets.UTF_16));

        // Читання вмісту файлу з указанням кодування UTF-16
        String read = Files.readString(Paths.get("utf16.txt"), StandardCharsets.UTF_16);
        System.out.println(read); // Коректне виведення початкового рядка
    }
}

Ключовий момент: під час запису й читання потрібно вказувати одне й те саме кодування. Якщо записати в UTF-16, а прочитати за замовчуванням (наприклад, у UTF-8), виникне «каша» з символів.

Особливості UTF-16

  • Фіксована довжина для більшості символів: 2 байти.
  • Сурогатні пари: частина символів (наприклад, емодзі) потребує 4 байти.
  • Byte Order Mark (BOM): на початку файлу часто додається спеціальна «шапка», що вказує порядок байтів (LE — little endian, BE — big endian). Java зазвичай автоматично розпізнає BOM.
  • Несумісний із ASCII: якщо ви відкриєте файл UTF-16 у редакторі, який очікує ASCII або UTF-8, побачите купу нулів і дивних знаків.

Коли використовувати UTF-16?

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

4. ISO-8859-1 (Latin-1) — привіт із минулого

ISO-8859-1, або Latin-1 — це однобайтне кодування, створене ще в далеких 80‑х роках минулого століття. Воно підтримує 256 символів: англійський алфавіт, західноєвропейські літери з діакритикою (é, ü, ç тощо), розділові знаки, спецсимволи.

Факт: У цьому кодуванні немає кирилиці, грецьких, арабських, китайських та інших не‑латинських символів.

Де трапляється ISO-8859-1?

  • У старих файлах і програмах (особливо в Європі).
  • У деяких базах даних і протоколах «за замовчуванням».
  • В HTTP‑заголовках (за стандартом текстові дані без указання кодування вважаються ISO-8859-1, хоча на практиці це рідко).

Приклад: як виглядає рядок у ISO-8859-1

Символ Код у Unicode ISO-8859-1 (байт) Коментар
A
U+0041
41
Те саме, що в ASCII
é
U+00E9
E9
Французький
ü
U+00FC
FC
Німецький
Я
U+042F
Немає такого символу!

Приклад використання в Java

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

public class Latin1Demo {
    public static void main(String[] args) throws Exception {
        String text = "Bonjour, ça va? Café!"; // Французький текст
        Files.write(Paths.get("latin1.txt"), text.getBytes(StandardCharsets.ISO_8859_1));

        // Читання файлу в ISO-8859-1
        String read = Files.readString(Paths.get("latin1.txt"), StandardCharsets.ISO_8859_1);
        System.out.println(read); // Усе читається коректно!

        // Спробуємо записати кирилицю
        String cyrillic = "Привіт, світ!";
        try {
            Files.write(Paths.get("badlatin1.txt"), cyrillic.getBytes(StandardCharsets.ISO_8859_1));
        } catch (Exception e) {
            System.out.println("Помилка: неможливо записати кирилицю в ISO-8859-1!");
        }
    }
}

Кодування ISO-8859-1 (Latin-1) підтримує тільки західноєвропейські символи — наприклад, літери з акцентами з французької, німецької чи іспанської мов. Тому французький текст записується і читається без проблем.

Однак кирилиці в цьому кодуванні немає, і під час спроби її записати виникає помилка або втрата символів. Саме тому для багатомовних текстів краще використовувати UTF-8 або UTF-16, які охоплюють значно більший набір символів.

Особливості ISO-8859-1

  • Обмеження за мовами: лише західноєвропейські мови.
  • 1 байт на символ: компактно, але дуже обмежено.
  • Запис «чужих» символів призводить до спотворення: якщо спробувати зберегти кирилицю або ієрогліфи, вони перетворяться на знаки питання або сміття.

Коли використовувати ISO-8859-1?

  • Тільки якщо ви інтегруєтеся з дуже старою системою або базою даних, яка потребує саме це кодування.
  • Для сучасних задач — не рекомендується.

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

Таблиця порівняння кодувань

Кодування Байтів на символ Які мови Сумісність із ASCII Де використовується
UTF-8
1–4 Усі Повна Веб, Java, JSON, XML, Linux
UTF-16
2 або 4 Усі Ні Усередині Java, Windows, XML
ISO-8859-1
1 Західна Європа Повна Старі програми, бази, HTTP

Як обрати кодування?

  • UTF-8 — ваш вибір для 99 % завдань. Сумісний із ASCII, підтримує всі мови, мінімальний розмір для англійського тексту, стандарт для вебу та Java.
  • UTF-16 — використовуйте лише, якщо того вимагає специфікація або інтеграція з Windows‑програмами.
  • ISO-8859-1 — тільки для зворотної сумісності зі старими системами. Ніколи не використовуйте для зберігання кирилиці, грецької, арабської тощо.

Як дізнатися, у якому кодуванні файл?

  • Відкрийте файл у редакторі, який показує кодування (наприклад, Notepad++, VS Code).
  • Якщо бачите «кракозябри», спробуйте відкрити файл в іншому кодуванні.
  • У терміналі Linux можна використати команду file ім’я_файлу — вона іноді підказує кодування.

Як задати кодування в Java?

Використовуйте класи з пакета java.nio.charset:

  • StandardCharsets.UTF_8
  • StandardCharsets.UTF_16
  • StandardCharsets.ISO_8859_1

Або через назву кодування:

Charset windows1251 = Charset.forName("Windows-1251");
Charset utf8 = Charset.forName("UTF-8");

6. Практика: що буде, якщо переплутати кодування?

Давайте спробуємо невеликий експеримент. Запишемо рядок у UTF-8, а потім спробуємо прочитати його як ISO-8859-1:

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

public class EncodingMismatchDemo {
    public static void main(String[] args) throws Exception {
        String text = "Привіт, світ!"; // Кирилиця

        // Записуємо як UTF-8
        Files.write(Paths.get("utf8demo.txt"), text.getBytes(StandardCharsets.UTF_8));

        // Читаємо як ISO-8859-1 (НЕВІРНО!)
        String wrong = Files.readString(Paths.get("utf8demo.txt"), StandardCharsets.ISO_8859_1);
        System.out.println("Невірне читання: " + wrong);

        // Тепер читаємо правильно
        String correct = Files.readString(Paths.get("utf8demo.txt"), StandardCharsets.UTF_8);
        System.out.println("Правильне читання: " + correct);
    }
}

Результат:

  • У першому випадку ви побачите «кракозябри» — набір дивних символів.
  • У другому випадку — нормальний український текст.

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

Помилка №1: Читання файлу в невірному кодуванні. Якщо файл записано в UTF-8, а ви читаєте його як ISO-8859-1 або Windows-1251, то всі не‑ASCII символи перетворяться на сміття. Це класика жанру: «у мене на комп’ютері все працює, а у колеги — ні».

Помилка №2: Спроба записати «чужі» символи в однобайтне кодування. Якщо ви спробуєте зберегти кирилицю або ієрогліфи в ISO-8859-1, Java замінить їх на знаки питання або видасть помилку. Дані буде втрачено безповоротно.

Помилка №3: Використання системного кодування «за замовчуванням». У деяких методах Java (наприклад, new FileReader("file.txt")) кодування не вказується явно — використовується системне. На одному комп’ютері це може бути UTF-8, на іншому — Windows-1251, на третьому — взагалі щось екзотичне. Тому завжди використовуйте методи, де можна явно вказати кодування.

Помилка №4: Відкриття файлу UTF-16 як UTF-8. Файл, записаний у UTF-16, якщо відкрити як UTF-8, міститиме купу нулів і дивних символів, тому що байти буде інтерпретовано невірно.

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