Ввод и вывод кириллицы в кодировке UTF-8

Написал программу, в которой выводится и вводится кириллица, используя кодировку 1251. Но пришло время перейти на UTF-8, чтоб расширить возможности красивого вывода (таблиц к примеру).

Простая замена кодировки файла и консоли не дала плодов, тогда я начал собирать информацию из разных источников и остановился на таком коде:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <Windows.h>


void main() {
    SetConsoleCP(65001);
    SetConsoleOutputCP(65001);

    setlocale(LC_ALL, "ru_RU.UTF-8");

    wprintf(L"Проверка┏━━┳\n");

    HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);

    wchar_t s[32], c;
    unsigned long read;
    unsigned int i;

    for (i = 0; i < 32; i++) {
        ReadConsole(handle, &c, 1, &read, NULL);

        if (c == '\n' || c == '\r') {
            i++;
            break;
        }
        else
            s[i] = c;
    }
    s[i - 1] = L'\0';

    wprintf(L"%ls", s);
}

Пришлось использовать и setlocale, и смену кодировки консоли, иначе не работает ввод или выводит не все символы юникода. Но я писал этот код в Visual Studio, а когда для проверки закинул в VSC и CodeBlocks, то обнаружил, что ничего не работает.
Если убрать смену кодировки, то тогда начинает работать ввод, но вывод - нет; если убрать смену локали, то оба метода коммуникации с пользователем перестают работать.

Почему так сильно отличается работа кода в VS и VSC, и как это можно исправить, если кодировка файлов одинаковая?


Ответы (1 шт):

Автор решения: zed

Если уж вы начали писать с использованием WinAPI, то пишите и ввод и вывод с его помощью:

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

int main() {
    SetConsoleCP(65001);
    SetConsoleOutputCP(65001);
        
    const size_t BUFFER_SIZE = 32;
    wchar_t* s = calloc(BUFFER_SIZE, sizeof(wchar_t));
        
    unsigned long read;
    HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
    if (ReadConsoleW(handle_in, s, BUFFER_SIZE, &read, NULL) == 0) {
        printf("Error: %ld", GetLastError());
        return -1;
    }
    
    if (read > 0) read--; // удаление перевода строки из инпута

    unsigned long written;
    HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteConsoleW(handle_out, s, read, &written, NULL);
}

Так же, не забывайте освобождать память, закрывать хендлы и обрабатывать ошибки в реальном коде.

P.S. Чтобы консоль могла печатать любые юникодные символы, в ней должен быть установлен соответствующий шрифт. Дефолтный шрифт не всегда удовлетворяет этому требованию.

→ Ссылка