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 для новых файлов — это де-факто стандарт, особенно если вы работаете с web, 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 или явно не закрыть поток, часть данных может не попасть на диск.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ