C++:套接字编码(使用 TeamSpeak)

C++: socket encoding (working with TeamSpeak)

由于我目前正在为 TeamSpeak 服务器开发一个程序,我需要检索当前在线用户的名称,我正在使用套接字进行这些操作 - 到目前为止工作正常。
在我的 UI 我在基本上可以工作的列表框中显示所有客户端。尽管如此,我还是遇到了 ListBox 中错误显示的字符和符号的问题。 我正在使用以下代码:

//...
auto getClientList() -> void{
    i = 0;
    queryString.str("");
    queryString.clear();
    queryString << clientlist << " \n";
    send(sock, queryString.str().c_str(), strlen(queryString.str().c_str()), NULL);
    TeamSpeak::getAnswer(1);
    while(p_1 != -1){
        p_1 = lastLog.find(L"client_nickname=", sPos + 1);
        if(p_1 != -1){
            sPos = p_1;
            p_2 = lastLog.find(L" ", p_1);
            temporary = lastLog.substr(p_1 + 16, p_2 - (p_1 + 16));
            users[i].assign(temporary.begin(), temporary.end());
            SendMessage(hwnd_2, LB_ADDSTRING, (WPARAM)NULL, (LPARAM)(LPTSTR)(users[i].c_str()));
            i++;
        }
        else{
            sPos = 0;
            p_1 = 0;
            break;
        }
    }
    TeamSpeak::getAnswer(0);
}
//...

我已经检查过 lastLogtemporaryusers[i](通过将它们写入文件),但是它们都没有字符或符号的编码问题(对于例如 Andrè)。如果我直接添加一个字符串:
SendMessage(hwnd_2, LB_ADDSTRING, (WPARAM)NULL, (LPARAM)(LPTSTR)L"Andrè",它在ListBox中正确显示。
这里可能是什么问题,是不是我的代码有问题还是别的?


更新 1:
我最近继续研究这个问题,并考虑从套接字接收单词 Olè!。我得到的结果如下:
O (79) | l (108) | � (-61) | � (-88) | ! (33).
如何将此 char array 转换为包含正确的 wstring字符数?


解决方案:
正如@isanae 在他的 post 中提到的,std::wstring_convert-模板为我解决了这个问题,谢谢非常喜欢!

这段代码中有很多地方可能出错,但您并没有表现出太多。特别缺乏的是所有这些变量的定义。

假设 users[i] 包含有意义的数据,您也不说明它是如何编码的。它是 ASCII 码吗? UTF-8? UTF-16?您可以将其输出到文件并使用编辑器读取这一事实并不意味着什么,因为大多数编辑器都能够猜测编码。

如果它确实是 UTF-16(Windows 上的本机编码),那么我认为没有理由让这段代码不起作用。一种检查方法是进入调试器并查看 users[i] 中的各个字节。如果您看到每个小于 128 的字符后跟一个 0,那么它可能是 UTF-16。

如果不是UTF-16,则需要进行转换。有多种方法可以做到这一点,但是 MultiByteToWideChar may be the easiest. Make sure you set the codepage to same encoding used by the sender. It may be CP_UTF8, or an actual codepage.

另请注意,使用非 ASCII 字符对字符串进行硬编码也无济于事,因为您首先必须找出文件本身的编码。我知道某些版本的 Visual C++ 如果遇到非 ASCII 字符,会将您的源文件转换为 UTF-16,这可能是您遇到的情况。

O (79) | l (108) | � (-61) | � (-88) | ! (33).

How can I convert this char array to a wstring containing the correct characters?

这是一个 UTF-8 字符串。它必须转换为 UTF-16 以便 Windows 可以使用它。

这是一个可移植的 C++11 解决方案,实现了 sizeof(wchar_t) == 2。如果不是这种情况,则可以使用 char16_tstd::u16string,但撰写本文时最新版本的 Visual C++ (2015 RC) 没有为 [ 实现 std::codecvt =19=] 和 char32_t.

#include <string>
#include <codecvt>

std::wstring utf8_to_utf16(const std::string& s)
{
    static_assert(sizeof(wchar_t)==2, "wchar_t needs to be 2 bytes");
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
    return conv.from_bytes(s);
}

std::string utf16_to_utf8(const std::wstring& s)
{
    static_assert(sizeof(wchar_t)==2, "wchar_t needs to be 2 bytes");
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
    return conv.to_bytes(s);
}

Windows-仅:

#include <string>
#include <cassert>
#include <memory>
#include <codecvt>
#include <Windows.h>

std::wstring utf8_to_utf16(const std::string& s)
{
    // getting the required size in characters (not bytes) of the
    // output buffer
    const int size = ::MultiByteToWideChar(
        CP_UTF8, 0, s.c_str(), static_cast<int>(s.size()),
        nullptr, 0);

    // error handling
    assert(size != 0);

    // creating a buffer with enough characters in it
    std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);

    // converting from utf8 to utf16
    const int written = ::MultiByteToWideChar(
        CP_UTF8, 0, s.c_str(), static_cast<int>(s.size()),
        buffer.get(), size);

    // error handling
    assert(written != 0);

    return std::wstring(buffer.get(), buffer.get() + written);
}

std::string utf16_to_utf8(const std::wstring& ws)
{
    // getting the required size in bytes of the output buffer
    const int size = ::WideCharToMultiByte(
        CP_UTF8, 0, ws.c_str(), static_cast<int>(ws.size()),
        nullptr, 0, nullptr, nullptr);

    // error handling
    assert(size != 0);

    // creating a buffer with enough characters in it
    std::unique_ptr<char[]> buffer(new char[size]);

    // converting from utf16 to utf8
    const int written = ::WideCharToMultiByte(
        CP_UTF8, 0, ws.c_str(), static_cast<int>(ws.size()),
        buffer.get(), size, nullptr, nullptr);

    // error handling
    assert(written != 0);

    return std::string(buffer.get(), buffer.get() + written);
}

测试:

// utf-8 string
const std::string s = {79, 108, -61, -88, 33};

::MessageBoxW(0, utf8_to_utf16(s).c_str(), L"", MB_OK);