2

Я хочу сделать корректный ввод/вывод юникод строк формата UTF-16LE в консоли Windows. Да, я знаю, что по умолчанию кодировка консоли - 866.

Мне необходимо это для упрощения работы с файлами, в именах которых могут содержаться любые символы юникода. Чтобы организовать удобную работу с файловой системой.

Проблема в том, что SetConsoleOutputCP() не принимает код 1200, который должен переводить консоль в режим UTF-16LE. В то же время, код 65001 корректно переводит консоль в режим UTF-8.

Kromster
  • 13,809
  • Консоль Windows поддерживает только однобайтовые кодировки. – Sergey Gornostaev Aug 29 '18 at 07:06
  • 1
  • @VTT, нет, это не дубликат вопроса. –  Aug 29 '18 at 07:24
  • @Sergey Gornostaev, откуда эта информация? Если это действительно так, тогда какой смысл было вводить для SetConsoleCP() и SetConsoleOutputCP() код установки UTF-16LE? –  Aug 29 '18 at 07:24
  • Это может и не совсем дубликат, зато, там есть решение, как выводить UTF-16 – user7860670 Aug 29 '18 at 07:31
  • @VVT, возможно вы не заметили, но мой вопрос относится к языку C и WinAPI, а не к C++. –  Aug 29 '18 at 07:32
  • 2
    Во-первых там С++ и не нужен, существенные функции _setmode и т.д. - это С функции. Во-вторых в тегах стоит cc++ – user7860670 Aug 29 '18 at 07:41
  • Посмотрите тут - варианты шаманских танцев с консолью в Виндоус, есть режим ".OCP" он же ОЕМ, смотреть в его сторону. – NewView Aug 29 '18 at 07:52
  • @VVT, я не совсем понимаю, что происходит в описанном решении, но кодировка консоли не меняется, а так же имена файлов с юникодом корректно в консоли не отображаются. –  Aug 29 '18 at 07:52
  • @NewView, костыльное решение мне не подходит, тогда уж проще просто работать с CP1251, а если имя файла содержит то, что не попадает в установленную кодовую страницу, то делать виноватый вид и разводить руками... Но хотелось бы, конечно, найти нормальное решение. Ядро Windows NT везде где можно использует UTF-16LE, но стандартная консоль этот формат юникода не поддерживает? Интересно... –  Aug 29 '18 at 07:54
  • Стандартная консоль нормально печатает только через виндовые специализированные функции из их АПИ, в них реализована привязки к кодировке консоли и перекодировки. Всякие POSIX с утф'ами работают отвратного, и отображают как придется. – NewView Aug 29 '18 at 07:58
  • Как вариант, самому писать низкоуровневую функцию вывода на базе их АПИ, где и реализовать нужное. По крайней мере я пришел именно к этому мнению при попытке нармализовать утф вывод в консоли. – NewView Aug 29 '18 at 08:02
  • Из всего набора функций, у MS с утф адекватно работает putwchar, рекомендую начать с нее. – NewView Aug 29 '18 at 08:06
  • Смотрите сюда: https://ru.stackoverflow.com/a/459299/10105 (дубликат?) – VladD Aug 29 '18 at 08:18
  • Нет, не дубликат. Описанные в приведенной ссылке проблемы и решения я уже применял и достаточно хорошо знаком с ними. Я все же пришел к тому, что, по всей видимости, нужно использовать юникодную точку входа wmain(), но в MinGW w64 с этим небольшие трудности. –  Aug 29 '18 at 10:23
  • @VladD По сути, автор справшивает, как вывести в UTF16 в консоль средствами WinAPI, без привязки к Visual C++. В том вопросе эта тема вроде не раскрыта. – MSDN.WhiteKnight Aug 29 '18 at 10:57
  • @Максим: Поскольку для MSVC решение есть и как бы известно, имеет смысл указать в вопросе вашу среду. – VladD Aug 30 '18 at 18:46
  • @MSDN.WhiteKnight: А, точно, я не подумал про MinGW, а в вопросе этого нет. – VladD Aug 30 '18 at 18:47

2 Answers2

2

Вот так:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <io.h>
#include <fcntl.h>

int main()
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    _setmode(_fileno(stderr), _O_U16TEXT);
    _setmode(_fileno(stdin), _O_U16TEXT);
    {
        wchar_t file_path[1024];
        int const items_count_to_scan = 1;
        if(items_count_to_scan == wscanf_s(L"%s", file_path, _countof(file_path)))
        {
            wprintf_s(L"got file path \"%s\"\n", file_path);
        }
    }
    _wsystem(L"pause");
    return 0;
}

Пример работы:

введите сюда описание изображения

user7860670
  • 29,796
  • У меня, почему-то, в описанной консоли текст FфԆᗇ в конце содержит символ P в квадратике, а сама консоль все равно находится в режиме CP866. Я очень ценю вашу помощь, но это решение не подойдет. Мне еще необходимо, чтобы в argv функции main()/wmain() была возможность принять корректные параметры в юникоде, например, ту же директорию запуска. –  Aug 29 '18 at 10:06
  • Возможно, это можно сделать, используя точку входа wmain(), но мой MinGW w64 на эту точку входа ругается. –  Aug 29 '18 at 10:07
  • Это возможно сделать переведя из char в wchar_t и потом принт. Через функцию mbstowcs например. – NewView Aug 29 '18 at 11:01
  • 1
    Кстати, у меня данный пример не работает на консоли, жаль скриншот прилепить нельзя в комментариях. – NewView Aug 29 '18 at 11:02
1

SetConsoleOutputCP не принимает UTF16, потому что в этом нет необходимости. Для вывода в UTF16 независимо от текущей кодовой страницы можно использовать функцию WriteConsoleW:

wchar_t str[]=L". ąęėšų\nEnglish -- Русский -- Ελληνικά -- Español.\n";
HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleW(consoleHandle, str, wcslen(str), NULL, NULL);

Что касается вывода через стандартную библиотеку С, для перевода его в режим UTF16 потребуется использовать нестандартные расширения компилятора, вроде _setmode в Microsoft CRT. Если ваш компилятор их не поддерживает, то ничего не получится.

  • Разве wprintf()/wscanf() не являются стандартными способами вывода/ввода? –  Aug 29 '18 at 11:24
  • А по поводу UTF-16LE и SetConsoleCP() - зачем тогда существует код 1200? В msdn написано, что этот код задает режим UTF-16LE. –  Aug 29 '18 at 11:25
  • @Максим Являются. Но практика, к сожалению, несколько расходится с теорией; под Windows, при использовании wprintf текст, по моим наблюдениям, не выводится в консоль в режиме UTF16, а преобразуются в текущую кодовую страницу. Можете сами проверить на примере моей строки с 4 языками - все символы, непредставимые в текущей локали, теряются. – MSDN.WhiteKnight Aug 29 '18 at 11:45
  • @Максим https://docs.microsoft.com/ru-ru/windows/desktop/Intl/code-page-identifiers "Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications" – MSDN.WhiteKnight Aug 29 '18 at 11:46
  • Так и придется использовать однобайтовые кодировки, с ними проблем в самом деле меньше, чем с юникодом. –  Aug 29 '18 at 12:36