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 | |
|
| Я | |
|
| € | |
|
| 😀 | |
|
Чому 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 | |
|
2 байти |
| Я | |
|
2 байти |
| € | |
|
2 байти |
| 😀 | |
|
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 | |
|
Те саме, що в ASCII |
| é | |
|
Французький |
| ü | |
|
Німецький |
| Я | |
— | Немає такого символу! |
Приклад використання в 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 | Де використовується |
|---|---|---|---|---|
|
1–4 | Усі | Повна | Веб, Java, JSON, XML, Linux |
|
2 або 4 | Усі | Ні | Усередині Java, Windows, XML |
|
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, міститиме купу нулів і дивних символів, тому що байти буде інтерпретовано невірно.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ