该问题中的问题已经解决,现在代码正在按预期进行,但utf-8到ucs2转换的最终输出是乱码。我的意思是最终文本的十六进制值与utf-8版本无关。我知道它们是不同的编码,但两者之间似乎没有任何映射。
转换输入为“ĩ”,输出为“ÿþ)^ A”。在十六进制中,“ĩ”(utf-8值)为c4a9,“ÿþ)^ A”(ucs2值)为“00FF 00FE 0029 0001”。
我希望有人对此行为有解释,或者可以告诉我在代码中我做错了什么。
新的更新代码是:
UErrorCode resultCode = U_ZERO_ERROR;
UConverter* pLatinOneConv = ucnv_open("ISO-8859-1", &resultCode);
// Change the callback to error out instead of the default
const void* oldContext;
UConverterFromUCallback oldFromAction;
UConverterToUCallback oldToAction;
ucnv_setFromUCallBack(pLatinOneConv, UCNV_FROU_CALLBACK_STOP, NULL, &oldFromAction, &oldContext, &resultCode);
ucnv_setToUCallBack(pLatinOneConv, UCNV_TO_U_CALLBACK_STOP, NULL, &oldToAction, &oldContext, &resultCode);
int32_t outputLength = 0;
int bodySize = uniString.length();
int targetSize = bodySize * 4;
char* target = new char[targetSize];
printf("Body: %s\n", uniString.c_str());
if (U_SUCCESS(resultCode))
{
outputLength = ucnv_fromAlgorithmic(pLatinOneConv, UCNV_UTF8, target, targetSize, uniString.c_str(),
uniString.length(), &resultCode);
ucnv_close(pLatinOneConv);
}
printf("ISO-8859-1 just tried to convert '%s' to '%s' with error '%i' and length '%i'", uniString.c_str(),
outputLength ? target : "invalid_char", resultCode, outputLength);
if (resultCode == U_INVALID_CHAR_FOUND || resultCode == U_ILLEGAL_CHAR_FOUND || resultCode == U_TRUNCATED_CHAR_FOUND)
{
if (resultCode == U_INVALID_CHAR_FOUND)
{
resultCode = U_ZERO_ERROR;
printf("Unmapped input character, cannot be converted to Latin1");
// segment Text, if necessary, and add UUIDs copy existing pPdu's addresses and optionals
UConverter* pUscTwoConv = ucnv_open("UCS-2", &resultCode);
if (U_SUCCESS(resultCode))
{
printf("Text Body: %s\n", uniString.c_str());
outputLength = ucnv_fromAlgorithmic(pUscTwoConv, UCNV_UTF8, target, targetSize, uniString.c_str(),
uniString.length(), &resultCode);
ucnv_close(pUscTwoConv);
}
printf("UCS-2 just tried to convert '%s' to '%s' with error '%i' and length '%i'", uniString.c_str(),
outputLength ? target : "invalid_char", resultCode, outputLength);
if (U_SUCCESS(resultCode))
{
pdus = SegmentText(target, pPdu, SEGMENT_SIZE_UNICODE_MAX, true);
}
}
else
{
printf("DecodeText(): Text contents does not appear to be valid UTF-8");
}
}
else
{
printf("DecodeText(): Text successfully converted to Latin1");
std::string newBody(target, outputLength);
pdus = SegmentText(newBody, pPdu, SEGMENT_SIZE_MAX);
}
答案 0 :(得分:2)
ICU转换可以为您提供正确的结果,但您不知道如何处理它们,并成功将它们转换为乱码。以下是你做错的事情,或多或少的顺序。
您在系统上打印非Latin-1数据(可用证据显示)本身在Latin-1中工作。
当您打印UTF-8时,这并不是很糟糕,因为UTF-8的设计不会破坏使用8位字符数据太难的内容。你会看到乱码,但至少你会看到你的所有数据,并且能够将它转换回合理的东西。
UTF-16(1996年取代UCS-2 的方式)并不那么友好。 UTF-16编码的字符串包含两个字节长的代码单元。这两个字节中的任何一个都能够为零。 (编码为UTF-16的所有ASCII字符都具有零字节)。只要另一个字节非零,整个字符就是非NULL。但是,您的printf
,strlen
等不知道是另一个字节。他们认为你正在给他们拉丁语-1,他们将停在第一个零字节(他们将其解释为NULL字符)。
幸运的是,ĩ
字符在其UTF-16编码中没有零字节,所以这次你已经离开了它。
如何正确完成?从不printf
或fputs
,但fwrite
/ std::ostream::write
;永远不会strcpy
,永远是memcpy
;永远不会strlen
,但始终将长度放在一个单独的变量中。
您在屏幕上打印此数据。
您的屏幕可以以不同且有趣的方式解释(大概)0到31,以及通常跟随它们的字节的字节。例如,移动光标,或者发出哔哔声或更改文本颜色。您正在打印UTF-16数据,该数据在其编码中绝对具有任何字节,即使源包含完全普通的可打印Unicode字符。所以任何事情都可能发生。
幸运的是,您尝试转换的单个字符在其UTF-16表示中不包含有害字节。
如何正确完成?如果您需要打印某些内容以便快速查看,请为所有或仅可打印的字符打印十六进制代码。
void print_bytes (FILE* fp, const unsigned char* s, int len,
bool escape_all) {
// note: explicit length, *never* strlen!
// note: unsigned char, you need it
int i;
for (i = 0; i < len; ++i, ++s)
{
if (escape_all || ! isprint(*s)) {
fprintf ("\\x%02x", *s);
}
else {
fputc(*s, fp);
}
}
}
你在fileinfo上查看你从屏幕上得到的Latin-1字符,然后将它们解释为Unicode字符,然后取出它们的16位字符代码(每个字符一个16位代码)并将它们解释为它们是字节。
没什么可说的。只是不要这样做。您有一个以可读的十六进制表示形式打印字节的函数。用它。或者,使用任意数量的免费程序显示甚至可以编辑这样的表示。
当然,这并不是说你不应该使用fileinfo。做得对,这基本上意味着知道你的编码是什么,以及字符的任何给定编码与其Unicode代码点的不同(尽管有时类似)。
本段不是关于错误本身,而是关于开发人员的直觉(或缺乏)与您发布的任何代码不对应。
尽管出现上述所有错误,您仍然设法获得几乎不错的数据。在所有偶数位置都有00,可能表示整数位大小有问题,你需要摆脱这些零。完成之后,您将使用FFFE作为前两个字节,您应该将其识别为BOM。您怀疑自己有一个字节序问题,但是您没有尝试通过改变UTF-16风格(UTF-16LE与UTF-16BE)来解决它。
任何Unicode开发人员都应该能够本能地应用这些东西。
Unicode大而复杂,比大多数人意识到的要复杂得多。这只是刚开始的一个开始。
请为此答案提出改进建议。