了解 Rad Studio 中的 UTF8Encode 和 UTF8ToString

Understanding UTF8Encode and UTF8ToString in Rad Studio

由于遗留原因,我需要在 Rad Studio 中使用使用 AnsiString 而不是 UnicodeString 的旧源。

所以,我的计划是大部分时间使用String,当我需要使用这个库时,转换成AnsiString,同样,尽快从AnsiString转换成String。

String temp = L"汉语/漢語";
AnsiString raw=UTF8Encode(temp);
String dest = UTF8ToString(raw);

当我检查时,我得到 ???而不是汉语/汉语。我做错了什么?

目前,我找到了解决方案:

AnsiString UTF8ToBin(UTF8String value)
{
   AnsiString dest;
   dest.SetLength(value.Length());
   memcpy(dest.c_str(), value.c_str(), value.Length());
   return dest;
}

它允许我使用使用 AnsiString 类型的库来存储二进制数据,在本例中为 UTF8。

我很清楚我需要对二进制数据使用 TBytes,但我无法重写库。

您需要避免在 Delphi/C++Builder 2009 及更高版本中使用 AnsiString 作为二进制缓冲区。它是一个代码页字符串,随着字符串的传递,可能会导致 subtle/unexpected 从一个代码页到另一个代码页的数据转换。在您的情况下,??? 是数据实际转换为不支持您尝试使用的 Unicode 字符的 Ansi 代码页的直接结果。

您确实应该对二进制数据使用 TBytes。对于 UTF-8 编码的字符串,请使用 UTF8String 代替:

String temp = L"汉语/漢語";
UTF8String raw = UTF8String(temp);
...
String dest = String(raw);

话虽如此,由于您必须与需要 UTF-8 编码 AnsiString 作为二进制缓冲区的第 3 方库互操作 1,那么您可以至少使用 UTF8String 变量并在将它传递给库时将其类型转换(而不是分配)到 AnsiString

library_function(*(reinterpret_cast<AnsiString*>(&raw));

或者:

library_function(reinterpret_cast<AnsiString&>(raw));

这是有效的,因为 AnsiStringUTF8StringRawByteString 都基于相同的 AnsiStringT 基本类型:

typedef AnsiStringT<0> AnsiString;
typedef AnsiStringT<65001> UTF8String;
typedef AnsiStringT<65535> RawByteString;

因此在后台共享一个共同的内存布局和实现,Delphi 会很好地接受这一点。

如果你真的想冒险,你应该更新库以使用 RawByteStringUTF8String(如果不是 TBytes)而不是 AnsiString,那么你根本不需要类型转换2:

library_function(raw);

1:您确实需要获取该库的新版本,或者使用不同的库。

2:这就是RawByteString原本打算针对的那种情况。它从未打算用于独立变量,而是用于可以接受任何类型的 8 位字符串作为输入而无需执行数据转换的函数参数。