Задать вопрос
@blecked88

Как считать из stdin русский текст и вывести его как wchar_t?

Есть вот такая программа:

#include <iostream>
#include <tchar.h>
#include <windows.h>

#define SZ 256

int main(void)
{
    setlocale(LC_ALL, "ru_RU.UTF-8");
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);

    TCHAR bufferW[SZ] = { 0 };
    fgetws(bufferW, SZ, stdin);

    _tprintf(L"Введенный текст: %s\n", bufferW);

    return 0;
}


Задача следующая: считать русские символы из консоли и иметь возможность оперировать ими как wchar_t, чтобы использоваться всякие приколы из винапи. Как настроить кодировки соответствующим образом? Я могу считывать и выводить и хранить char русские символы, но wchar_t не умею.

Программа выше выдает такое:

hpO1OmQ.png
  • Вопрос задан
  • 140 просмотров
Подписаться 1 Простой 4 комментария
Решения вопроса 1
@blecked88 Автор вопроса
Проблема в итоге решилась так.

#include <stdio.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>

#define SZ 256

int main(void)
{
    int _ = _setmode(_fileno(stdin), _O_U16TEXT);
    _ = _setmode(_fileno(stdout), _O_U16TEXT);

    TCHAR input[SZ] = { 0 };

    _tprintf(L"Введите текст: ");
    fgetws(input, SZ, stdin); input[wcslen(input) - 1] = 0;
    _tprintf(L"Введенны текст: %s", input);

    return 0;
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
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...
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы