Как перестать бояться кодировок в Java — лайфхак для новичков

от автора

Автор статьи: Сергей Прощаев (@sproshchaev)
Руководитель направления Java‑разработки в FinTech

Введение

Когда вы работаете с текстовыми файлами в Java, особенно содержащими кириллические символы, то важно правильно управлять кодировкой. Ошибки в кодировке приводят к искажению текста, появлению квадратных символов или нечитаемых строк. В этой статье мы разберём примеры чтения и записи файлов с кириллицей, используя базовые классы ввода и вывода в Java.

Основы кодировки в Java

Кодировка или encoding — это способ представления текстовых символов в виде байтов. Распространённые кодировки:

  1. UTF-8 — универсальная, поддерживает все языки, включая кириллицу,

  2. Windows-1251 — часто используется в Windows‑файлах,

  3. 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 три:

  1. ошибка чтения/записи файла — при которых либо файл не существует (возникает FileNotFoundException из подкласса IOException), либо может быть недостаточно прав для доступа к этому файлу или файл может быть поврежден;

  2. сетевые ошибки — которые свидетельствуют о проблемах с подключением к серверу, либо об ошибках передачи данных по сети;

  3. ну и наконец могут возникать ошибки при работе с потоками — сюда входят ошибки закрытия потока и ошибка чтения из потока.

А как определить кодировку файла?

Если вы не знаете, в какой кодировке сохранён файл, то можно использовать библиотеки, такие как 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. Присоединяйтесь — будет полезно.


ссылка на оригинал статьи https://habr.com/ru/articles/926026/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *