【问题标题】:C - WinAPI: Why does UTF-8 encoded characters show as Chinese in Listview?C - WinAPI:为什么 UTF-8 编码的字符在 Listview 中显示为中文?
【发布时间】:2019-09-08 22:19:41
【问题描述】:

尝试将文本添加到列表视图。 尽管多次尝试正确使用编码,但代码仍显示为中文字符。 json文件到listview的代码总结:

// json file (UTF8 without BOM)
"name": "abcdefghijklmnop" // shows as Chinese
// "name": "ササササササササササ" // Japanese does not show correctly neither

// declare structure and array; malloc array later
struct user {
    char* name;
    // char[16] name;
};
struct user *users;

// read from json
cJSON* json_name = cJSON_GetObjectItemCaseSensitive(json_user, "name");

// set name (in a loop)
users[i].name = json_name->valuestring;

// create listview
HWND hWndListView = CreateWindowExW(NULL,
    WC_LISTVIEW,
    L"Test Listview",
    WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS,
    // ...

// add column definitions
LVCOLUMN lvc = { 0 };
lvc.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_FMT;
lvc.fmt = LVCFMT_LEFT;

// column example
lvc.iSubItem = 0;
lvc.cx = 100;
lvc.pszText = TEXT("A");
ListView_InsertColumn(hWndListView, 0, &lvc);

// debug: check it's unicode
BOOL what = IsWindowUnicode(hWndListView); // 1

// debug
#ifdef UNICODE
    int i = 0; // this is executed
#endif

我尝试过的:

ListView_SetItemText(hWndListView, 2, 1, servers[0].name);         // chinese
ListView_SetItemText(hWndListView, 2, 1, *servers[0].name);        // access violation
ListView_SetItemText(hWndListView, 2, 1, (LPWSTR)servers[0].name); // chinese
ListView_SetItemText(hWndListView, 2, 1, L"%s", servers[0].name);  // %s
ListView_SetItemText(hWndListView, 3, 0, TEXT(servers[0].name));   // does not compile

在 VS2019 调试器中,添加一个带有“s8”参数的手表以查看结果,因为 UTF8 正确显示了文本。 只有在控件中添加时,它才会正确显示。 请注意,这是有效的:

ListView_SetItemText(hWndListView, 2, 2, TEXT("ササ"));

问题:我缺少什么才能正确显示我的数据?

【问题讨论】:

    标签: c listview winapi encoding


    【解决方案1】:

    您在俗称的“Mojibake”中所体验的内容。当 8 位字符数据被误解为 16 位 Unicode 数据时,就会发生这种情况。

    由于您使用了CreateWindowExW(),您正在为您的 ListView 创建一个 Unicode 窗口。因此,您必须使用 wchar_t 字符为 ListView 提供正确的 UTF-16 编码文本,但您通过 char* 指针为其提供 UTF-8 编码文本,该指针仅被类型转换为 wchar_t*。指向的字符数据本身仍然是 UTF-8,而不是 UTF-16。

    您需要将 UTF-8 编码的 char 数据实际转换为 UTF-16 编码的 wchar_t 数据,例如使用 MultiByteToWideChar()(或等效),例如:

    struct user {
        wchar_t* name;
        // wchar_t name[16];
    };
    
    ...
    
    int len = MultiByteToWideChar(CP_UTF8, 0, json_name->valuestring, -1, NULL, 0);
    users[i].name = (wchar_t*) malloc(len * sizeof(wchar_t));
    MultiByteToWideChar(CP_UTF8, 0, json_name->valuestring, -1, users[i].name, len);
    
    ...
    
    ListView_SetItemText(hWndListView, 2, 1, users[0].name);
    
    ...
    
    free(users[i].name);
    

    TEXT("ササ") 之所以有效,是因为您在项目中定义了UNICODE,因此TEXT() 在其输入字符串文字前加上L 前缀,即L"ササ",使其成为正确的UTF-16 编码wchar_t字符串字面量。

    【讨论】:

    • 我使用的是 UTF8 的 cJSON,并且不知何故开始认为当前的标准到处都是 UTF8,但事实并非如此。 JSON 的默认值为 UTF8,但 16 和 32 也有效。不知道 WinAPI 是 UTF16...谢谢!
    • "开始认为当前的标准到处都是 UTF8,但事实并非如此" - 不,事实上大多数平台(嗯,至少大多数库 API 与系统 API)在 UTF-16 而不是 UTF-8 上更加标准化。您会发现许多第 3 方库更喜欢 UTF-16 进行文本处理,因为它更易于使用,但与 UTF-8 相比,它会占用更多内存。 UTF-8 通常最适合存储和通信,但不太适合文本处理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-08
    • 2011-03-29
    • 1970-01-01
    • 2016-01-11
    • 1970-01-01
    • 2020-02-17
    相关资源
    最近更新 更多