【问题标题】:Convert UTF-16LE to UTF-8 in C在 C 中将 UTF-16LE 转换为 UTF-8
【发布时间】:2015-01-14 20:19:08
【问题描述】:

我正在使用一个库,该库具有一个函数,该函数在标准 char * 中返回编码为 UTF-16LE(我很确定)的结果字符串,以及字符串中的字节数。我想将这些字符串转换为 UTF-8。我尝试了这个问题的解决方案:Convert UTF-16 to UTF-8 under Windows and Linux, in C,它说要使用 iconv,但结果是输入和输出缓冲区都清空了。我错过了什么?

我的输入和输出缓冲区的声明和初始化如下:

char *resbuff=NULL;
char *outbuff=NULL;
int stringLen;
size_t outbytes=1024;
size_t inbytes;
size_t convResult;
...
//some loop and control code here
...
if (resbuff==NULL) {
    resbuff=(char *)malloc(1024);
    outbuff=(char *)malloc(1024);
}

然后我调用库函数用数据填充rebuff。查看调试器中的缓冲区,我可以看到缓冲区中的数据。例如,如果数据是“测试”,我会在查看 rebuff 的各个索引时看到以下内容:

't','\0','e','\0','s','\0','t','\0'

我认为是 UTF-16LE(使用相同库的其他代码似乎可以证实这一点),并且 stringlen 现在等于 8。然后我尝试使用以下代码将其转换为 UTF-8:

iconv_t conv;
conv=iconv_open("UTF-8", "UTF-16LE");
inbytes=stringLen;
convResult=iconv(conv,&resbuff,&inbytes,&outbuff,&outbytes); //this does return 0
iconv_close(conv);

结果 outbuff 和 resbuff 都以空字符串结束。

请注意,我将 stringlen 声明为 int 而不是 unsigned long,因为这是库函数所期望的。

编辑:我根据下面 John Bollinger 的回答稍微调整了我的代码,但并没有改变结果。

编辑 2:最终,此代码的输出将在 Python 中使用,因此我认为虽然它可能更丑陋,但我将在那里执行字符串转换。它只是工作。

【问题讨论】:

  • 我认为它已经在 C 中为你工作了。你误解了结果。很可能,您错过了 iconv() 更新输出缓冲区指针值的含义。
  • @JohnBollinger 所以也许原来的输出缓冲区有预期的输出,只是指针不再指向原来的?
  • 是的,正是如此。 iconv() 应该让它指向缓冲区中紧跟转换后的数据的位置。这就是为什么,正如我在更新的答案中所包含的那样,如果您实际上需要保留原始指针的值(这并不总是需要,但您可能确实需要)。
  • @JohnBollinger 我对此进行了一些测试,看来这就是答案。我声明的初始输出缓冲区最终确实保存了正确转换的字符串 - 我只是丢失了指向所述缓冲区的指针。

标签: c encoding utf-8 character-encoding utf-16le


【解决方案1】:

您没有显示变量stringLenoutbytes 的声明或初始化,您的问题很可能就在那儿。不过这...

请注意,我将 stringlen 声明为 int 而不是 unsigned long,因为这是库函数所期望的。

... 非常麻烦。 iconv() 函数期望它的第三个和第五个参数是size_t * 类型,如果它们实际上是不同的类型,通过强制转换向编译器撒谎不会使代码真正工作。你应该有这些方面的东西:

size_t in_bytes_left = (expression giving the total input length, in bytes);
size_t out_bytes_available = (expression giving the size of the output buffer);
char *input_temp = resbuff;
char *output_temp = outbuff;
int result;

result = iconv(conv, &input_temp, &in_bytes_left, &output_temp, &out_bytes_available);

还要注意,您应该检查返回值以确保转换完成且成功(在这种情况下,返回值将 >= 0)。如果它小于零,那么调用之后的errno 的值会告诉你发生了什么样的问题。

编辑添加:

你原来说零字节被转换了,现在你说

outbuff 和 resbuff 都以空字符串结束。

这根本不是一回事。

iconv() 函数更新指向输入和输出缓冲区的指针,以方便通过多次调用转换长输入,这种需求相当普遍。这就是为什么您必须将指针传递给这些指针。如果您不想丢失这些指针的原始值,那么您应该制作并传递副本;我已经更新了上面的代码来证明这一点。

此外,iconv() 返回错误指示符或不可逆转换字符的计数,而不是转换字符总数的计数。对于有效的 UTF-16{,LE,BE} 到 UTF-8,不应该有任何不可逆的转换。返回值为零表示指定数量的输入字节全部成功且可逆地转换为输出字节。

还要注意,resbuff 至少从来都不是 C 字符串。嵌入在数据中的空字符使字符串解释不合适。但是,根据您的输入和输出缓冲区的初始化方式,可能是在iconv() 完成后,*resbuff == '\0'*outbuff == '\0'(参考您自己的当前代码)。顺便说一句,我将这些“空”字符串称为“空”字符串。如果您的意思是 iconv() 离开 resbuff == 0outbuff == 0(即 NULL 指针),那么这将构成 iconv() 中的错误。

【讨论】:

  • 我确实显示了 stringlen 的声明,我只是在这里输入我的代码时打错了大写 l。但是,我确实忽略了显示输出字节的声明。我将进行编辑以解决这些问题。
  • 好的,我根据您的建议更新了我的代码和问题,因此我为两个参数传递了 size_t 并检查了返回码。我确实得到了 0,所以它似乎认为转换正在工作,但事实并非如此。
  • 是的,调用 rebuff 一个 C 字符串是不合适的 - 但它确实存在。库调用需要一个 char ** 作为参数,并用指定的字符串填充它。这就是我必须解决的问题。如果我的术语有误,我深表歉意,但是是的:缓冲区最后填充了空值,它们不会成为空指针。正如我所说,iconv 确实返回 0,所以它认为它正在转换所有字符,但输出缓冲区仍然充满了 null。不知道。
  • 哦,我从来没有说过“零字节被转换”我所说的是输出缓冲区中有零字节。你是对的 - 这些不是一回事。这就是为什么我从不说第一个。
  • 抱歉,由于您编辑了问题,我不得不从内存中工作,但是在这种情况下,“输出缓冲区中的零字节”相当于“转换了零字节”,因为 UTF-16 没有任何班次序列。
猜你喜欢
  • 1970-01-01
  • 2022-06-14
  • 2014-08-14
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
相关资源
最近更新 更多