【问题标题】:C - How to convert wide char Japanese characters to UTF-8?C - 如何将宽字符日文字符转换为 UTF-8?
【发布时间】:2020-02-03 05:30:18
【问题描述】:

尝试将存储在宽字符中的日文字符转换为 UTF-8,以便使用 cJSON 库将值存储在 json 文件中。首先尝试使用wcstombs_s,但显然这不支持日文字符:

size_t len = wcslen(japanese[i].name) + 1;
char* japanese_char = malloc(len);
if (japanese_char == NULL) {
    exit(EXIT_FAILURE);
}
size_t sz;
wcstombs_s(&sz, japanese_char, len, japanese[i].name, _TRUNCATE);

然后,根据其他答案,也是在成功从json UTF-8转换为wide char的情况下,尝试了相反的功能,但目标缓冲区dest只包含垃圾字符:

size_t wcsChars = wcslen(japanese[i].name);
size_t sizeRequired = WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, wcsChars, NULL, 0, NULL, NULL);
char* dest = calloc(sizeRequired, 1);
WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, wcsChars, dest, sizeRequired, NULL, NULL);
free(dest);

我要转换的宽字符 (wchar_t) 是存储在 japanese[i].name 中的 ササササササササササササササササ(结构中的 wchar_t*)。目标是使用 cJSON 的 cJSON_CreateString 将值保存在 UTF-8 编码的 json 文件中。

问题:在 C(不是 C++)中将日语从 wchar_t 转换为 UTF-8 char 的正确方法是什么?

【问题讨论】:

    标签: c json encoding utf-8 cjson


    【解决方案1】:

    您的wcstombs_s() 代码将错误的值传递给sizeInBytes 参数:

    sizeInBytes

    mbstr 缓冲区的大小(以字节为单位)。

    您传递的是japanese[i].name字符 计数,而不是japanese_char 分配的字节 计数。它们的值不同。

    Unicode 代码点以 UTF-16 编码(wchar_t 字符串在 Windows 上编码为),每个使用 2 或 4 个字节,而在 UTF-8 中,每个使用 1-4 个字节,具体取决于它们的值。 U+0080..U+FFFF 范围内的 Unicode 代码点在 UTF-8 中比在 UTF-16 中占用更多字节,因此您的 japanese_char 缓冲区实际上可能需要分配比您的 japanese[i].name 数据更大的空间。就像您可以调用 WideCharToMultiByte() 来确定所需的目标缓冲区大小一样,您也可以使用 wcstombs_s() 执行相同的操作。

    size_t len = 0;
    wcstombs_s(&len, NULL, 0, japanese[i].name, _TRUNCATE);
    if (len == 0)
        exit(EXIT_FAILURE);
    char* japanese_char = malloc(len);
    if (!japanese_char)
        exit(EXIT_FAILURE);
    wcstombs_s(&len, japanese_char, len, japanese[i].name, _TRUNCATE);
    ...
    free(japanese_char);
    

    由于您将显式大小传递给 cchWideChar 参数,因此您的 WideCharToMultiByte() 代码不是以空值结尾的 dest

    cchWideChar

    lpWideCharStr 指示的字符串的大小(以字符为单位)。或者,如果字符串以 null 结尾,则可以将此参数设置为 -1。如果 cchWideChar 设置为 0,则函数失败。

    如果此参数为 -1,则函数处理整个输入字符串,包括终止空字符。因此,生成的字符串有一个终止空字符,函数返回的长度包括这个字符。

    如果此参数设置为正整数,则函数会精确处理指定数量的字符。如果提供的大小不包含终止空字符,则生成的字符串不是空终止字符,并且返回的长度不包含该字符。

    cJSON_CreateString() 需要一个以 null 结尾的 char* 字符串。所以你需要:

    • calloc()num 参数添加+1 以说明缺少的空终止符。
    size_t wcsChars = wcslen(japanese[i].name);
    size_t len = WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, wcsChars, NULL, 0, NULL, NULL);
    char* japanese_char = malloc(len + 1);
    if (!japanese_char)
        exit(EXIT_FAILURE);
    WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, wcsChars, japanese_char, len, NULL, NULL);
    japanese_char[len] = '\0';
    ...
    free(japanese_char);
    
    • wcslen() 的返回值上加+1,或将WideCharToMultiByte()cchWideChar 参数设置为-1,以在输出中包含空终止符。
    size_t wcsChars = wcslen(japanese[i].name) + 1;
    size_t len = WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, wcsChars, NULL, 0, NULL, NULL);
    if (len == 0)
        exit(EXIT_FAILURE);
    char* japanese_char = malloc(len);
    if (!japanese_char)
        exit(EXIT_FAILURE);
    WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, wcsChars, japanese_char, len, NULL, NULL);
    ...
    free(japanese_char);
    
    size_t len = WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, -1, NULL, 0, NULL, NULL);
    if (len == 0)
        exit(EXIT_FAILURE);
    char* japanese_char = malloc(len);
    if (!japanese)
        exit(EXIT_FAILURE);
    WideCharToMultiByte(CP_UTF8, 0, japanese[i].name, -1, japanese_char, len, NULL, NULL);
    ...
    free(dest);
    

    【讨论】:

    • 它可以工作(我也忘记将,s8 添加到 VS 监视窗口以查看 UTF8 编码值,但如果没有你的修复它是错误的:我有 ササササササササササササササササ6Cfp)。再次感谢您的详细解答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    • 2016-04-03
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    相关资源
    最近更新 更多