我正在尝试打印出一串 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?
更新
在评论中纳入了一些建议。
更新代码:
// *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 个字节。
最佳答案
这是一个将 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
之前将输入缓冲区中剩余的任何字节复制到新输入缓冲区的开头。关于c - 如何将 UTF-16 转换为 UTF-32 并在 C 中打印结果 wchar_t?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8465939/