Автор статьи: Сергей Прощаев (@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. Присоединяйтесь — будет полезно.

Комментарии (1)


  1. aleksandy
    09.07.2025 03:52

    Когда-то давно @Skipy написал о кодировках гораздо более содержательную и полезную статью.