Печать wchar_t на консоль в C ++ - PullRequest
       70

Печать wchar_t на консоль в C ++

2 голосов
/ 09 февраля 2020

Я сделал простую программу, которая будет читать двоичный файл карты памяти PS1 и отображать его содержимое в консоли с помощью C ++ в Visual Studio. Названия игр кодируются в памяти в формате Shift-JIS, поэтому я использовал функцию MultiByteToWideChar для их преобразования:

            // Converting Shift-JIS
            char lTitle[65];
            strcpy_s(lTitle, mymemcard[lFrame - 1].title);

            int lTitleChars = MultiByteToWideChar(932, 0, lTitle, -1, NULL, 0);
            wchar_t* lTitleL = new wchar_t[lTitleChars];
            MultiByteToWideChar(932, 0, lTitle, -1, lTitleL, lTitleChars);

Моя проблема в том, что теперь я не могу получить переменную lTitleL для печати в приставка. Я пробовал cout, wcout, printf, wprintf, я не могу заставить их работать! Я знаю, что переменная lTitleL proiperly содержит заголовок, так как я вижу его в отладчике. Когда я вызываю любую из функций печати, которые я пробовал, просто ничего не печатается. Любая помощь будет оценена! Спасибо.

БОЛЬШОЕ РЕДАКТИРОВАНИЕ:

ОК, вот где я с этим:

            // Converting Shift-JIS to UTF-8 //
            ///////////////////////////////////
            // First, convert the multi-byte Shift-JIS format into wide characters
            strcpy_s(lTitle, mymemcard[lFrame - 1].titleMB);
            lTitleChars = MultiByteToWideChar(932, 0, lTitle, -1, NULL, 0);
            wchar_t* lTitleFW = new wchar_t[lTitleChars];
            MultiByteToWideChar(932, 0, lTitle, -1, lTitleFW, lTitleChars);
            wprintf(L"FW, WriteConsoleW - ");
            WriteConsoleW(consoleHandle, lTitleFW, lTitleChars, NULL, 0);
            wprintf(L"\n");
            // Memory card seems to store all characters in their "Full Width" forms, we need to convert them to Half-Width so they display nicely in the console
            wchar_t* lTitleHW = new wchar_t[lTitleChars];
            LCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_HALFWIDTH, lTitleFW, lTitleChars, lTitleHW, lTitleChars, NULL, NULL, 0);
            wprintf(L"HW, WriteConsoleW - ");
            WriteConsoleW(consoleHandle, lTitleHW, lTitleChars, NULL, 0);
            wprintf(L"\n");
            wprintf(L"HW, wprintf()     - %s\n", lTitleHW);
            // Now we can convert it into UTF-8 format to allow it to be printed using std::cout
            // This step isn't necessary as we could call WriteConsoleW on lTitleHW instead, but why not
            lTitleChars = WideCharToMultiByte(CP_UTF8, 0, lTitleHW, -1, NULL, 0, NULL, NULL);
            char* lTitleUTF = new char[lTitleChars];
            WideCharToMultiByte(CP_UTF8, 0, lTitleHW, -1, lTitleUTF, lTitleChars, NULL, NULL);
            strcpy_s(mymemcard[lFrame - 1].titleUTF, lTitleUTF);
            wprintf(L"UTF, wprintf()    - %S\n\n", lTitleUTF);
            delete[] lTitleFW;
            delete[] lTitleHW;
            delete[] lTitleUTF;
            ///////////////////////////////////
  • MultiByteToWideChar, чтобы получить строку ввода Shift-JIS в широкий chars.
  • Выведите это на консоль для отладки.
  • Проблема теперь в том, что на карте памяти PS1 все символы хранятся в полноразмерных формах, поэтому я использую LCMapStringEX для их преобразования в полуширину. для более приятного вывода.
  • Выведите это на консоль для отладки.
  • Этого достаточно, если я с удовольствием использую WriteConsoleW, но если нет, то одна из проблем заключается в том, что определенные знаки препинания кодируются странно и не красиво печатать используя std :: wcout или wprintf (). Например, дефисы сохраняются как U + FF70 - «Полуширинная звуковая метка Katakana-Hiragana Prolonged Sound Mark» (после преобразования в половинную ширину), и они отображаются в виде вопросительных знаков независимо от используемого шрифта (однако они печатаются правильно используя WriteConsoleW).
  • Итак, теперь я могу использовать WideCharToMultiByte, используя кодовую страницу UTF-8, чтобы получить версию строки, которая хорошо печатается на консоли, используя std :: wcout или wprintf (). Однако для правильной печати мне необходимо вызвать и SetConsoleCP (65001), и SetConsoleOutputCP (65001), в противном случае многобайтовые символы (такие как U + FF70, о которых я упоминал) печатаются побайтово.
  • Наконец, для отображения любых необычных символов мне нужно выбрать шрифт, который может их отображать. Я обнаружил, что единственными шрифтами, которые работают со шрифтами по умолчанию в моей консоли, являются NSimSun и SimSun-ExtB, и ни один из остальных, похоже, не содержит японские символы (по крайней мере, для символа U + FF70).

Чтобы помочь визуализировать вывод этого, вот скриншот консоли, использующей шрифт NSimSun:

enter image description here

А здесь со шрифтом Consolas:

enter image description here

Теперь на мой вопрос: мне не особенно нравится шрифт NSimSun, есть ли другие, которые больше похожи на шрифт Consolas, которые содержат все символы для полной / половинной ширины японских символов? Если так, как я могу упаковать их в мое консольное приложение, чтобы оно было переносимым?

Спасибо!

1 Ответ

1 голос
/ 11 февраля 2020

Windows имеет ограниченную поддержку для печати Unicode на консоли. Протестируйте ваше преобразование, используя MessageBoxW(0, lTitleL, 0, 0), который гарантированно будет успешным, если преобразование было в порядке.

Чтобы напечатать в окне консоли, вы должны вызвать _setmode(_fileno(stdout), _O_U16TEXT); для печати символов вне диапазона ASCII ,

Придерживайтесь std::wcout или wprintf(L"%s", str)

Существует дополнительная проблема, если консоль не поддерживает правильный шрифт. Вы должны попробовать разные шрифты. Смотрите пример ниже. Также рассмотрите возможность использования std::wstring, которая имеет автоматическую очистку c.

#include <iostream>
#include <string>
#include <io.h> 
#include <fcntl.h> 
#include <Windows.h>

int main()
{
    const wchar_t *src = L"ABC 日本語";

    int size;
    size = WideCharToMultiByte(932, 0, src, -1, NULL, 0, 0, 0);
    std::string temp(size, 0);
    WideCharToMultiByte(932, 0, src, -1, temp.data(), size, 0, 0);

    size = MultiByteToWideChar(932, 0, temp.data(), -1, 0, 0);
    std::wstring dst(size, 0);
    MultiByteToWideChar(932, 0, temp.c_str(), -1, dst.data(), size);

    _setmode(_fileno(stdout), _O_U16TEXT);

    wprintf(L"%s\n", dst.c_str());

    //Try with different font
    CONSOLE_FONT_INFOEX font = { sizeof(font) };
    HANDLE hcout = GetStdHandle(STD_OUTPUT_HANDLE);
    GetCurrentConsoleFontEx(hcout, FALSE, &font);
    wcscpy_s(font.FaceName, L"MS Gothic");
    SetCurrentConsoleFontEx(hcout, FALSE, &font);
    std::wcout << dst << "\n";

    return 0;
}
...