【问题标题】:UTF-8 text in MFC application that uses Multibyte character setMFC 应用程序中使用多字节字符集的 UTF-8 文本
【发布时间】:2019-06-26 08:14:43
【问题描述】:

我正在开发一个应用程序,它接收以 UTF-8 编码的文本并需要在某些 MFC 控件上显示它。该应用程序是使用多字节字符集 (MBCS) 构建的,我们假设这无法更改。

我希望如果我将接收到的文本从 UTF-8 转换为宽字符字符串,我将能够使用 SetWindowTextW 方法正确显示它。为了尝试这个,我使用了一个玩具应用程序,它从文件中读取输入并设置我的控件的文本。

std::wstring utf8_decode(const std::string &str)
{
    if (str.empty()) return std::wstring();
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

BOOL CAboutDlg::OnInitDialog()
{
    std::vector<std::string> texts;
    texts.resize(6);
    std::fstream f("D:\\code\\sample-utf8.txt", std::ios::in);
    for (size_t i=0;i<6;++i)
        std::getline(f, texts[i]);

    ::SetWindowTextW(GetDlgItem(IDC_BUTTON1)->m_hWnd, utf8_decode(texts[0]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON2)->m_hWnd, utf8_decode(texts[1]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON3)->m_hWnd, utf8_decode(texts[2]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON4)->m_hWnd, utf8_decode(texts[3]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON5)->m_hWnd, utf8_decode(texts[4]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON6)->m_hWnd, utf8_decode(texts[5]).c_str());
    return TRUE;
}

使用 MBCS 构建了玩具应用程序后,我没有得到我想要的东西。

我使用 unicode 构建应用程序后,一切正常

这是否意味着当我使用 MBCS 构建时,没有希望对单个控件使用 unicode 文本?如果可能的话,你能给我一些指示吗?谢谢。

【问题讨论】:

  • 由于 MFC 窗口是 MBCS,请改用 SetWindowTextA(),并像以前一样将 UTF-8 数据转换为 UTF-16,然后在传递之前使用 WideCharToMultiByte(CP_ACP) 将 UTF-16 转换为 MBCS到窗户。但是您最终可能会得到相同的结果,因为将 Unicode 转换为 MBCS 是有损的。这就是你不应该再使用 MBCS 的原因
  • 您可能需要为窗口设置一个 unicode 字体——可以处理 unicode 字符。如果您只有一个对话框,您可以在代码中重新创建子窗口以删除旧的 MBCS 窗口并为这些子窗口使用 unicode 窗口。
  • 您的代码应该可以正常工作。也许您的文本文件不是 UTF8。使用记事本检查编码。或者这是@JosephWillcoxson 指出的字体问题 - 对于 Windows Vista 及更高版本,将字体设置为 Segoe UI
  • 他说如果他将应用程序设为 Unicode,它就可以工作。那会告诉我文本文件可能没问题。如果控件中的字体不能处理 unicode,那么它可能有他看到的那个问题。
  • 感谢您的提示。 @JosephWillcoxson 我会尝试您的建议以编程方式创建它们,这似乎是最好的选择。

标签: c++ unicode utf-8 mfc multibyte-characters


【解决方案1】:

MBCS 应用程序创建 MBCS 窗口,一般来说,即使您使用宽字符串界面,也只能显示单个代码页中的文本。

对于 MBCS 应用程序,SetWindowTextW 的宽字符串版本本质上是使用用户的当前语言环境(具有默认代码页)将宽字符串转换为 MBCS,然后将其传递给函数的 -A 版本。

正如您在“Unicode”实验中看到的那样,总的来说,您做的事情是正确的,但您会受到应用程序是 MBCS 这一事实的限制。

【讨论】:

  • 我在 VS 2008 上试过这个,我设置了 MBCS,并启用了视觉样式,::SetWindowTextW 正确显示了 Unicode 文本。如果我禁用视觉样式(正如提问者所做的那样),那么我得到?? 代替?。我从来没有弄清楚 MBCS,我想我永远不会。
  • 我明白了,现在事情变得更有意义了,感谢您的解释!
【解决方案2】:

感谢 Adrian McCarthy 和 Joseph Willcoxson 的建议,我得到了这项工作。我使用CreateWindowExW 方法创建控件,然后使用SetWindowTextW 设置文本。以下是示例代码,以防有任何帮助:

std::wstring utf8_decode(const std::string &str)
{
    if (str.empty()) return std::wstring();
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

HWND CreateButtonW(int x, int y, int width, int height, HWND parent)
{
    HWND hwndButton = ::CreateWindowExW(
        WS_EX_CLIENTEDGE,
        L"BUTTON",  // Predefined class; Unicode assumed 
        L"", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
        x, y, width, height, parent,
        NULL, // No menu.
        (HINSTANCE)GetWindowLong(parent, GWL_HINSTANCE),
        NULL);      // Pointer not needed.
    return hwndButton;
}

BOOL CAboutDlg::OnInitDialog()
{
    std::vector<std::string> texts;
    texts.resize(6);
    std::fstream f("D:\\code\\sample-utf8.txt", std::ios::in);
    for (size_t i=0;i<6;++i)
        std::getline(f, texts[i]);


    ::SetWindowTextW(GetDlgItem(IDC_BUTTON1)->m_hWnd, utf8_decode(texts[0]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON2)->m_hWnd, utf8_decode(texts[1]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON3)->m_hWnd, utf8_decode(texts[2]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON4)->m_hWnd, utf8_decode(texts[3]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON5)->m_hWnd, utf8_decode(texts[4]).c_str());
    ::SetWindowTextW(GetDlgItem(IDC_BUTTON6)->m_hWnd, utf8_decode(texts[5]).c_str());

    auto width  = [](RECT& r) { return r.right - r.left; };
    auto height = [](RECT& r) { return r.bottom - r.right; };

    RECT r;
    GetDlgItem(IDC_BUTTON1)->GetWindowRect(&r); ScreenToClient(&r);
    HWND hBtnWnd = CreateButtonW(r.right+20, r.top, width(r), height(r), m_hWnd);
    ::SetWindowTextW(hBtnWnd, utf8_decode(texts[0]).c_str());

    GetDlgItem(IDC_BUTTON2)->GetWindowRect(&r); ScreenToClient(&r);
    hBtnWnd = CreateButtonW(r.right+20, r.top, width(r), height(r), m_hWnd);
    ::SetWindowTextW(hBtnWnd, utf8_decode(texts[1]).c_str());

    GetDlgItem(IDC_BUTTON3)->GetWindowRect(&r); ScreenToClient(&r);
    hBtnWnd = CreateButtonW(r.right+20, r.top, width(r), height(r), m_hWnd);
    ::SetWindowTextW(hBtnWnd, utf8_decode(texts[2]).c_str());

    GetDlgItem(IDC_BUTTON4)->GetWindowRect(&r); ScreenToClient(&r);
    hBtnWnd = CreateButtonW(r.right+20, r.top, width(r), height(r), m_hWnd);
    ::SetWindowTextW(hBtnWnd, utf8_decode(texts[3]).c_str());

    GetDlgItem(IDC_BUTTON5)->GetWindowRect(&r); ScreenToClient(&r);
    hBtnWnd = CreateButtonW(r.right+20, r.top, width(r), height(r), m_hWnd);
    ::SetWindowTextW(hBtnWnd, utf8_decode(texts[4]).c_str());

    GetDlgItem(IDC_BUTTON6)->GetWindowRect(&r); ScreenToClient(&r);
    hBtnWnd = CreateButtonW(r.right+20, r.top, width(r), height(r), m_hWnd);
    ::SetWindowTextW(hBtnWnd, utf8_decode(texts[5]).c_str());

    return TRUE;
}

结果——左边是默认创建的按钮,右边是使用CreateWindowExW创建的按钮:

【讨论】:

    猜你喜欢
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 2014-11-05
    • 1970-01-01
    • 2020-08-08
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    相关资源
    最近更新 更多