【问题标题】:How to Convert UTF-16 to UTF-32 and Print the Resulting wchar_t in C?如何将 UTF-16 转换为 UTF-32 并在 C 中打印生成的 wchar_t?
【发布时间】:2012-01-17 22:51:37
【问题描述】:

我正在尝试打印一串 UTF-16 字符。我不久前发布了这个问题,给出的建议是使用 iconv 转换为 UTF-32 并将其打印为 wchar_t 字符串。

我做了一些研究,并设法编写了以下代码:

// *c is the pointer to the characters (UTF-16) i'm trying to print
// sz is the size in bytes of the input i'm trying to print

iconv_t icv;
char in_buf[sz];
char* in;
size_t in_sz;
char out_buf[sz * 2];
char* out;
size_t out_sz;

icv = iconv_open("UTF-32", "UTF-16");

memcpy(in_buf, c, sz);

in = in_buf;
in_sz = sz;
out = out_buf;
out_sz = sz * 2;

size_t ret = iconv(icv, &in, &in_sz, &out, &out_sz);
printf("ret = %d\n", ret);
printf("*** %ls ***\n", ((wchar_t*) out_buf));

iconv 调用总是返回 0,所以我猜转换应该没问题?

然而,印刷似乎是偶然的。有时,转换后的 wchar_t 字符串打印正常。其他时候,它似乎在打印 wchar_t 时遇到了问题,并且完全终止了 printf 函数调用,以至于即使是尾随的“***”也不会被打印出来。

我也尝试过使用

wprintf(((wchar_t*) "*** %ls ***\n"), out_buf));

但什么都没有打印出来。

我错过了什么吗?

参考:How to Print UTF-16 Characters in C?

更新

在 cmets 中加入了一些建议。

更新代码:

// *c is the pointer to the characters (UTF-16) i'm trying to print
// sz is the size in bytes of the input i'm trying to print

iconv_t icv;
char in_buf[sz];
char* in;
size_t in_sz;
wchar_t out_buf[sz / 2];
char* out;
size_t out_sz;

icv = iconv_open("UTF-32", "UTF-16");

memcpy(in_buf, c, sz);

in = in_buf;
in_sz = sz;
out = (char*) out_buf;
out_sz = sz * 2;

size_t ret = iconv(icv, &in, &in_sz, &out, &out_sz);
printf("ret = %d\n", ret);
printf("*** %ls ***\n", out_buf);
wprintf(L"*** %ls ***\n", out_buf);

仍然是相同的结果,不是所有的 UTF-16 字符串都被打印(包括 printf 和 wprintf)。

我还能缺少什么?

顺便说一句,我使用的是 Linux,并且已经验证 wchar_t 是 4 个字节。

【问题讨论】:

  • wprintf() 需要格式字符串具有 L 前缀,例如wprintf(L"*** %ls ***\n", out_buf).
  • 为什么要将输入复制到本地缓冲区in_buf?直接用c就行了……
  • 您也不能合法地将指向char 数组的指针转换为指向wchar_t 的指针。输出缓冲区的类型必须为wchar_t [n]
  • 并非所有平台都对wchar_t 使用UTF-32,Win 没有。
  • 在 Linux 上,您不能在同一个应用程序中混合宽 (wprintf) 和窄 (printf) 输出。第一次调用设置方向并且不能更改后记。 “一旦流具有方向,它就无法更改并持续存在,直到流关闭。”见linux.about.com/library/cmd/blcmdl3_fwide.htmbytes.com/topic/c/answers/…

标签: c utf-16 iconv utf-32


【解决方案1】:

这是一个将 UTF-16 转换为宽字符数组然后打印出来的小程序。

#include <endian.h>
#include <errno.h>
#include <iconv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#define FROMCODE "UTF-16"

#if (BYTE_ORDER == LITTLE_ENDIAN)
#define TOCODE "UTF-32LE"
#elif (BYTE_ORDER == BIG_ENDIAN)
#define TOCODE "UTF-32BE"
#else
#error Unsupported byte order
#endif

int main(void)
{
    void *tmp;
    char *outbuf;
    const char *inbuf;
    long converted = 0;
    wchar_t *out = NULL;
    int status = EXIT_SUCCESS, n;
    size_t inbytesleft, outbytesleft, size;
    const char in[] = {
        0xff, 0xfe,
        'H', 0x0,
        'e', 0x0,
        'l', 0x0,
        'l', 0x0,
        'o', 0x0,
        ',', 0x0,
        ' ', 0x0,
        'W', 0x0,
        'o', 0x0,
        'r', 0x0,
        'l', 0x0,
        'd', 0x0,
        '!', 0x0
    };
    iconv_t cd = iconv_open(TOCODE, FROMCODE);
    if ((iconv_t)-1 == cd) {
        if (EINVAL == errno) {
            fprintf(stderr, "iconv: cannot convert from %s to %s\n",
                    FROMCODE, TOCODE);
        } else {
            fprintf(stderr, "iconv: %s\n", strerror(errno));
        }
        goto error;
    }
    size = sizeof(in) * sizeof(wchar_t);
    inbuf = in;
    inbytesleft = sizeof(in);
    while (1) {
        tmp = realloc(out, size + sizeof(wchar_t));
        if (!tmp) {
            fprintf(stderr, "realloc: %s\n", strerror(errno));
            goto error;
        }
        out = tmp;
        outbuf = (char *)out + converted;
        outbytesleft = size - converted;
        n = iconv(cd, (char **)&inbuf, &inbytesleft, &outbuf, &outbytesleft);
        if (-1 == n) {
            if (EINVAL == errno) {
                /* junk at the end of the buffer, ignore it */
                break;
            } else if (E2BIG != errno) {
                /* unrecoverable error */
                fprintf(stderr, "iconv: %s\n", strerror(errno));
                goto error;
            }
            /* increase the size of the output buffer */
            converted = size - outbytesleft;
            size <<= 1;
        } else {
            /* done */
            break;
        }
    }
    converted = (size - outbytesleft) / sizeof(wchar_t);
    out[converted] = L'\0';
    fprintf(stdout, "%ls\n", out);
    /* flush the iconv buffer */
    iconv(cd, NULL, NULL, &outbuf, &outbytesleft);
exit:
    if (out) {
        free(out);
    }
    if (cd) {
        iconv_close(cd);
    }
    exit(status);
error:
    status = EXIT_FAILURE;
    goto exit;
}

由于 UTF-16 是一种可变长度编码,因此您猜测输出缓冲区需要多大。一个正确的程序应该能够处理输出缓冲区不足以容纳转换后的数据的情况。

您还应该注意iconv 不会为您终止输出缓冲区NULL

Iconv 是一个面向流的处理器,因此如果您想将它重用于另一个转换,则需要刷新 iconv_t(示例代码在接近尾声时执行此操作)。如果您想进行流处理,您将处理 EINVAL 错误,将输入缓冲区中剩余的所有字节复制到新输入缓冲区的开头,然后再次调用 iconv

【讨论】:

    猜你喜欢
    • 2016-07-23
    • 2012-03-30
    • 2023-03-18
    • 2014-07-16
    • 2019-05-15
    • 1970-01-01
    • 2020-01-28
    • 2015-09-21
    相关资源
    最近更新 更多