理解Rad Studio中的UTF8Encode和UTF8ToString

3

由于历史原因,我需要使用一份旧的源代码,该代码在Rad Studio中使用AnsiString而不是UnicodeString。

所以我的计划是大部分时间使用String,只有当我需要使用这个库时,将其转换为AnsiString,然后尽快从AnsiString转换为String。

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

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


1
你做错的事情是使用 AnsiString。你需要停止这样做。你需要使用 Unicode 编码来保存你的数据。ANSI 无法完成任务。可以使用 UTF-16 或 UTF-8,但不能使用 ANSI。如果没有了解遗留动机,很难指导你找到解决方案。 - David Heffernan
1
使用 AnsiString 存储二进制数据是非常古老的方法,是在动态数组 (of Byte) 出现之前的一种糟糕的做法。 - Free Consulting
1
啊,这个库是用Delphi写的吗?好的,这会让你的事情变得有点复杂。在2009年之后的Delphi版本(以及可能的C++Builder版本)中,当你使用AnsiString时会遇到麻烦。 - Rudy Velthuis
1
如果你不能重写代码,我想你就需要放弃了。要么这样,要么回到 Delphi 的一个 Unicode 版本之前。另一方面,3DES 是相当常规的。有许多不错的库可以执行此加密操作。这些库不会混淆二进制和文本数据。也许是时候超越这个设计不良的库了。 - David Heffernan
1
此外,我认为 AnsiString 和 UTF8Encode 不同。UTF8Encode 使用 65001 代码页,而 AnsiString 使用 0 页(在 Windows 中定义)。 - kokokok
显示剩余9条评论
2个回答

3
您需要避免在Delphi/C++Builder 2009及以后版本中使用AnsiString作为二进制缓冲区。这是一种编码过的字符串,会导致隐晦/意外的数据转换从一个代码页到另一个,因为字符串被传递。在您的情况下,???是实际上被转换为不支持您尝试使用的Unicode字符集的Ansi代码页的直接结果。
您真正应该使用TBytes来处理二进制数据。对于UTF-8编码的字符串,请改用UTF8String
String temp = L"汉语/漢語";
UTF8String raw = UTF8String(temp);
...
String dest = String(raw);

话虽如此,由于您需要与期望作为二进制缓冲区的 UTF-8 编码 AnsiString 进行交互的第三方库进行互操作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位字符串作为输入而不进行数据转换。


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

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

它允许我使用AnsiString类型来存储二进制数据的库,这种情况下是UTF8。

我非常清楚需要使用TBytes来处理二进制数据,但我无法重写该库。


3
为什么不直接使用RawByteString,而要使用AnsiString?将UTF8String 赋值给 RawByteString 可以保留数据,而将其赋值给 AnsiString 不能。可以使用 RawByteString raw = UTF8Encode(temp);RawByteString raw = UTF8String(temp); (因为UTF8Encode()已弃用)。如果库只接受特定的 AnsiString,而不是通用的 RawByteString,则可以使用类型转换:UTF8String raw = temp; library(*(AnsiString*)&raw);,因为 (Ansi|Utf8|Raw)String 共享相同的内存布局和实现,它们都基于 AnsiStringT - Remy Lebeau
1
太好了,library(*(AnsiString*)&raw) 是另一种解决方案。谢谢。 - kokokok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接