blecked88, Было свободное время, провел несколько экспериментов, т.к. про _setmode впервые у тебя увидел.
Если вместо _O_U16TEXT использовать _O_U8TEXT, то ввод/вывод будет преобразовываться в UTF8, что более привычно (чем UTF16). Особенно это заметно при выводе строк (ввод как раз лучше оставить в UTF16) в файл (например если в консоли сделать перенаправление вывода в файл, а затем файл открыть в текстовом редакторе).
Вообще, учитывая, что кодировку консоли может устанавливать пользователь самостоятельно, то чтоб было совсем красиво надо перекодировать выводимые строки в кодировку консоли, а не принудительно устанавливать свою кодировку через setmode/SetConsoleCP и т.п..
Накидал пример, как это может выглядеть:
Пример#define _CRT_SECURE_NO_WARNINGS
#include <stddef.h>
#include <stdint.h>
#include <locale.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <io.h>
#include <fcntl.h>
#include <Windows.h>
UINT cpo = 0;
void print_wide(const wchar_t* wstr)
{
assert(cpo != 0);
static char buf[512];
if(WideCharToMultiByte(cpo, 0, wstr, -1, buf, 512, NULL, NULL) != 0)
printf("multibyte string: \"%s\"\n", buf);
else
printf("Error WideCharToMultiByte()\n");
}
void main()
{
cpo = GetConsoleOutputCP();
_setmode(_fileno(stdin), _O_U16TEXT);
wchar_t wbuf[512];
if (wscanf(L"%511ls", wbuf) == 1)
print_wide(wbuf);
else
printf("Error wscanf()\n");
// const wchar_t *str = L"Привет мир!\u00df\u6c34\U0001f34c";
// print_wide(str);
}
Схема такая: внутри программы все строки хранятся как wchar_t, литералы с префиксом L. Для консольного ввода принудительно выставляем преобразование в wchar_t с помощью _setmode(U16TEXT) и используем фунции w/wcs. Настройки консольного вывода не меняем, а используем установленные пользователем или системой.
Для вывода строк:
1. в начале main узнаем текущую кодовую страницу консоли
2. в функции print_wide применяем преобразование кодировки с помощью стандартной функции WinAPI.
Пример взят
отсюда. И адаптирован для ввода. Функция преобразования заменена, т.к. функция wcsrtombs из стандартной библиотеки преобразовывает только в UTF8, но не в другие доступные в консоли винды кодировки.
Таким образом благодаря _setmode и функциям w/wcs ввод автоматически преобразовывается в wchar_t, а вывод "руками" преобразовывается в текущую кодировку консоли.
В онлайн компиляторах пример гонять бесполезно - не понятно какие кодировки у них там при запуске.
При компиляции в msvc файл с исходником сохранять в UTF8, добавить ключ компилятора /utf-8 (или можно сохранить файл с BOM).
Для тестов меняем кодировки консоли руками (chcp 866/1251/65001), перенаправляем вывод в файл и смотрим кодировку текста в файле каким-нибудь доступным редактором (notepad++/far edit/...) - она должна совпадать с кодировкой установленной командой chcp.
К слову, когда последний раз несколько лет назад писал консольную утилиту для винды, я применял такой же подход с перекодировкой. Только перекодировал в ручную и ввод и вывод, т.к. не знал про _setmode. В приведенном примере перекодировку ввода удачно можно переложить на стандартную библиотеку.
Еще немного в тему. Для файлового текстового ввода/вывода можно применять этот же подход, только кодировку входного файла надо устанавливать вручную (параметром командной строки или еще как-то) или анализировать текст файла, есть алгоритмы для автоматического определения кодировки, правда они не всегда могут срабатывать.
Статьи на тему:
https://habr.com/ru/companies/xakep/articles/257895/
https://habr.com/ru/companies/ruvds/articles/645325/
https://habr.com/ru/articles/731614/
Полезные ссылки:
https://learn.microsoft.com/ru-ru/windows/console/...
https://learn.microsoft.com/ru-ru/windows/win32/in...
https://learn.microsoft.com/ru-ru/windows/win32/ap...
https://learn.microsoft.com/ru-ru/cpp/c-runtime-li...
https://learn.microsoft.com/ru-ru/cpp/c-runtime-li...