Автор статьи: Сергей Прощаев (@sproshchaev)
Руководитель направления Java‑разработки в FinTech
Введение
Когда вы работаете с текстовыми файлами в Java, особенно содержащими кириллические символы, то важно правильно управлять кодировкой. Ошибки в кодировке приводят к искажению текста, появлению квадратных символов или нечитаемых строк. В этой статье мы разберём примеры чтения и записи файлов с кириллицей, используя базовые классы ввода и вывода в Java.
Основы кодировки в Java
Кодировка или encoding — это способ представления текстовых символов в виде байтов. Распространённые кодировки:
UTF-8 — универсальная, поддерживает все языки, включая кириллицу,
Windows-1251 — часто используется в Windows‑файлах,
KOI8-R — устаревшая кодировка для русского языка.
Если кодировка файла не совпадает с кодировкой, указанной при чтении или записи, текст будет отображаться некорректно.
Предположим, у вас есть текстовый файл с именем input.txt, который содержит строку на кириллице «Привет, мир!»
Чтобы прочитать этот файл — давайте напишем метод, в котором укажем кодировку UTF-8
import java.io.*;
public class ReadFile {
public static void main(String[] args) {
String filePath = "input.txt";
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(filePath), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
В этом примере экземпляр класса InputStreamReader читает байты из файла и декодирует их в символы с помощью указанной кодировки «UTF-8». Через BufferedReader мы построчно считываем текст. Если файл сохранён в другой кодировке — например, Windows-1251, то замените в этом примере «UTF-8» на «Windows-1251» и содержимое файла input.txt будет корректно выведено в консоль.
Теперь выполним обратную операцию и напишем код для записи кириллического текста в файл, используя кодировку UTF-8:
import java.io.*;
public class WriteFile {
public static void main(String[] args) {
String filePath = "output.txt";
String text = "Здравствуйте, мир!";
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8"))) {
writer.write(text);
System.out.println("Текст успешно записан в файл.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Этот Java‑код записывает строку «Здравствуйте, мир!» в текстовый файл output.txt с использованием кодировки UTF-8. Здесь filePath — имя файла, куда будет записан текст, а text — строка с кириллицей, которую нужно сохранить в файл. Экземпляр класса FileOutputStream(filePath) открывает поток для записи байтов в файл, а OutputStreamWriter(..., «UTF-8») преобразует байты в символы, используя кодировку UTF-8. Это важно для корректного отображения кириллицы. В примере мы используем экземпляр класса BufferedWriter, который ускоряет запись, буферизуя данные перед их записью в файл. И для этих инструкций используется обертка в try‑with‑resources, которая автоматически закрывает все ресурсы после завершения работы с ними, даже если произойдёт ошибка.
Далее через метод writer.write(text) строка text записывается в файл и в консоль выводится сообщение об этом.
Теперь давайте разберем что происходит в блоке catch{...} и что такое класс IOException в этих примерах:
} catch (IOException e) {
e.printStackTrace();
}
Класс IOException в Java — это исключение, которое возникает при ошибках, связанных с операциями ввода‑вывода (I/O). Оно принадлежит к пакету java.io и является проверяемым исключением (checked exception), то есть его необходимо обрабатывать с помощью блока try‑catch или пробрасывать через throws. В наших примерах мы используем первый вариант.
Основных причин, из‑за которых может возникнуть IOException три:
ошибка чтения/записи файла — при которых либо файл не существует (возникает FileNotFoundException из подкласса IOException), либо может быть недостаточно прав для доступа к этому файлу или файл может быть поврежден;
сетевые ошибки — которые свидетельствуют о проблемах с подключением к серверу, либо об ошибках передачи данных по сети;
ну и наконец могут возникать ошибки при работе с потоками — сюда входят ошибки закрытия потока и ошибка чтения из потока.
А как определить кодировку файла?
Если вы не знаете, в какой кодировке сохранён файл, то можно использовать библиотеки, такие как juniversalchardet от Mozilla или любые другие аналогичные. Для этого давайте добавим ее в наш проект в pom.xml для использования сборщиком Maven:
<dependency>
<groupId>com.googlecode.juniversalchardet</groupId>
<artifactId>juniversalchardet</artifactId>
<version>1.0.3</version>
</dependency>
Теперь давайте добавим в наш первый пример, в котором мы читаем текстовый файл с именем input.txt, который содержит строку на кириллице «Привет, мир!».
Для этого давайте для начала определим статический метод определения кодировки в файле:
private static String detectEncoding(String filePath) {
UniversalDetector detector = new UniversalDetector(null);
try (InputStream inputStream = new FileInputStream(filePath)) {
byte[] buf = new byte[4096];
int nread;
while ((nread = inputStream.read(buf)) > 0 && !detector.isDone()) {
detector.handleData(buf, 0, nread);
}
detector.dataEnd();
} catch (IOException e) {
e.printStackTrace();
}
String encoding = detector.getDetectedCharset();
detector.reset();
// Если кодировка не определена - использовать UTF-8 по умолчанию
return encoding != null ? encoding : "UTF-8";
}
}
И добавим использование метода detectEncoding() в наш первый пример:
import java.io.*;
import org.mozilla.universalchardet.UniversalDetector;
public class ReadFile {
public static void main(String[] args) {
String filePath = "input.txt";
// Определение кодировки файла
String encoding = detectEncoding(filePath);
System.out.println("Обнаруженная кодировка: " + encoding);
// Чтение файла с обнаруженной кодировкой
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(filePath), encoding))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
Заключение
Работа с кириллицей в Java требует внимательного подхода к кодировке. Указывайте кодировку явно при чтении и записи файлов или используйте библиотеки для определения неизвестной кодировки, это поможет избежать искажений и сделать ваш код более переносимым.
Если вы только начинаете путь в Java и хотите разобраться в одной из самых частых проблем при работе с текстом, приглашаем вас на открытый урок «Кракозябры vs Java: как победить кодировки и стать Гуру Unicode?».
Занятие пройдёт в рамках курса «Java‑разработчик. Специализация с нуля».
Поговорим о том, почему при чтении и записи файлов появляются искажённые символы, как работает система кодировок и что нужно знать, чтобы не терять данные на этапе работы с текстом.
Урок состоится 17 июля в 20:00. Присоединяйтесь — будет полезно.
aleksandy
Когда-то давно @Skipy написал о кодировках гораздо более содержательную и полезную статью.