Как-то раз, при разработке очередной автоматизации, мне понадобилось создавать директории (папки) в операционной системе Windows. Само приложение, работает тоже на Windows. Проблема заключалась в том, что JAVA не корректно обрабатывает букву И. Возникала ошибка

error: unmappable character for encoding Cp1251

Код был примерно следующим:

String dd = "D:\\АБВГДЕЁЖЗКИЙЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ\\SDF";
File theDir = new File(new String(dd.getBytes("Cp1251"), "UTF-8"));
createFolder(theDir);

public static boolean createFolder(File theDir) {
    String fileSeparator = java.nio.file.FileSystems.getDefault().getSeparator();
    String namefile = "asdasd";
    System.out.println("Разделитель ФС: " + fileSeparator);

    // если директория не существует, создаем ее
    if (!theDir.exists() && !theDir.isDirectory()) {
        System.out.println("Создаем папку: " + theDir.getName());
        boolean result = false;

        try {
            result = theDir.mkdir();
        } catch (SecurityException se) {
            System.out.println("SecurityException" + se);
            return false;
        }
        if (result) {
            System.out.println("Папка создана");
            return true;
        } else {
            System.out.println("Не удалось создать папку");
            return false;
        }
    } else {
        System.out.println("папка уже существует");
        return true;
    }
}

Всяческие «костыли» не помогали. Проблема заключается в том, что String находится изначально в кодировке utf-16.

Буква И кодируется в utf-8 как последовательность байтов 0xd0, 0x98

Код 0xd0 соответствует символу Р в кодировке cp1251, а вот код 0x98 не соответствует никакому символу, он просто отсутствует в кодировке, поэтому вместо несуществующего символа будет подставлен заменяющий. Получится строка Р�.

Cимвол � тоже отсутствует в cp1251, поэтому при повторном кодировании будет заменен на ? (с кодом 0x3f), получаем последовательность байтов 0xd0, 0x3f.

При декодировании сталкиваемся еще с одной проблемой: последовательность 0xd0, 0x3f недопустима в utf-8, поэтому теперь уже вместо 0xd0 будет подставлен заменяющий символ, в итоге получается строка �?

С остальными буквами такого может и не произойти, но это не значит, что подобные манипуляции всегда будут приводить к правильному результату.

Решение было найдено!!! И это… Kotlin!!!

Раз уж эти языки совместимы между собой, почему не использовать преимущества того, что работает лучше, быстрее, и требует написания меньше кода?

Исходный вариант на Kotlin-языке был такой:

private fun createFold(theDir: String) { val fileSeparator = FileSystems.getDefault().separator val words = theDir.split("" + fileSeparator + "").toTypedArray() println("Папок: " + words.size) val path = StringBuilder() //тут лежат пути var nowPath: String? = null for (i in words.indices) { path.append(words[i]).append(fileSeparator) val theDirs = File(path.toString()) println("Проверка пути: $theDirs") // если директория не существует, создаем ее if (!theDirs.exists() && !theDirs.isDirectory) { println("Создаем папку: " + theDirs.name) var result = false try { result = theDirs.mkdir() } catch (se: SecurityException) { println("SecurityException$se") } if (result) { println("Папка создана") } else { println("Не удалось создать папку. Проверьте права на запись в директории $nowPath") break } } else { path.append(fileSeparator) nowPath = path.toString() println("папка уже существует") } } }

И это сработало!! Проблем с созданием папок на кириллице больше не возникало. Буду рад, если это кому-то помогло.