gpt4 book ai didi

delphi - 从 RawByteString 转换为字符串会自动调用 UTF8Decode 吗?

转载 作者:行者123 更新时间:2023-12-03 15:09:08 25 4
gpt4 key购买 nike

我想将任意二进制数据作为 BLOB 存储到 SQlite 数据库中。

数据将通过此函数添加为:

procedure TSQLiteDatabase.AddParamText(name: string; value: string);

现在我想将 WideString 转换为其 UTF8 表示形式,以便可以将其存储到数据库中。调用UTF8Encode并将结果存储到数据库后,我注意到数据库内的数据不是UTF8解码的。相反,它在我的计算机区域设置中被编码为 AnsiString。

我运行了以下测试来检查发生了什么:

type
{$IFDEF Unicode}
TBinary = RawByteString;
{$ELSE}
TBinary = AnsiString;
{$ENDIF}

procedure TForm1.Button1Click(Sender: TObject);
var
original: WideString;
blob: TBinary;
begin
original := 'ä';
blob := UTF8Encode(original);

// Delphi 6: ä (as expected)
// Delphi XE4: ä (unexpected! How did it do an automatic UTF8Decode???)
ShowMessage(blob);
end;

字符“ä”转换为 UTF8 后,内存中的数据是正确的(“¤”),但是,一旦我将 TBinary 值传递给函数(如stringAnsiString),Delphi XE4 出于某种我不知道的原因执行了调用 UTF8Decode 的“神奇类型转换”。

我已经找到了避免这种情况的解决方法:

function RealUTF8Encode(AInput: WideString): TBinary;
var
tmp: TBinary;
begin
tmp := UTF8Encode(AInput);
SetLength(result, Length(tmp));
CopyMemory(@result[1], @tmp[1], Length(tmp));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
original: WideString;
blob: TBinary;
begin
original := 'ä';
blob := RealUTF8Encode(original);

// Delphi 6: ä (as expected)
// Delphi XE4: ä (as expected)
ShowMessage(blob);
end;

但是,使用 RealUTF8Encode 的解决方法对我来说看起来很脏,我想了解为什么简单调用 UTF8Encode 不起作用,以及是否有更好的解决方案。

最佳答案

在 Delphi 的 Ansi 版本(D2009 之前)中,UTF8Encode() 返回 UTF-8 编码的 AnsiString。在 Unicode 版本(D2009 及更高版本)中,它返回一个 UTF-8 编码的 RawByteString,并为其分配了 CP_UTF8 (65001) 代码页。

在 Ansi 版本中,ShowMessage() 采用 AnsiString 作为输入,而 UTF-8 字符串是 AnsiString,因此它得到按原样显示。在 Unicode 版本中,ShowMessage() 采用 UTF-16 编码的 UnicodeString 作为输入,因此 UTF-8 编码的 RawByteString 会转换为 UTF- 16 使用其指定的 CP-UTF8 代码页。

如果您实际上将 blob 数据直接写入数据库,您会发现它可能是也可能不是 UTF-8 编码,具体取决于您的写入方式。但你的做法是错误的;在这种情况下,使用 RawByteString 是不正确的。 RawByteString 仅用作过程参数。不要将其用作局部变量。这就是你的问题的根源。来自 documentation :

The purpose of RawByteString is to reduce the need for multiple overloads of procedures that read string data. This means that parameters of routines that process strings without regard for the string's code page should typically be of type RawByteString.

RawByteString should only be used as a parameter type, and only in routines which otherwise would need multiple overloads for AnsiStrings with different codepages. Such routines need to be written with care for the actual codepage of the string at run time.

对于 Unicode 版本的 Delphi,我建议您使用 TBytes 来代替 RawByteString 来保存 UTF-8 数据,并使用 TEncoding 对其进行编码:

var
utf8: TBytes;
str: string;
...
str := ...;
utf8 := TEncoding.UTF8.GetBytes(str);

您正在寻找一种在传递时不执行隐式文本编码的数据类型,而 TBytes 就是这种类型。

对于 Ansi 版本的 Delphi,您可以完全按照您的方式使用 AnsiStringWideStringUTF8Encode

但就我个人而言,我建议对 UTF-8 数据一致使用 TBytes。因此,如果您需要一个支持 Ansi 和 Unicode 编译器的单一代码库(呃!),那么您应该创建一些帮助程序:

{$IFDEF Unicode}
function GetUTF8Bytes(const Value: string): TBytes;
begin
Result := TEncoding.UTF8.GetBytes(Value);
end;
{$ELSE}
function GetUTF8Bytes(const Value: WideString): TBytes;
var
utf8str: UTF8String;
begin
utf8str := UTF8Encode(Value);
SetLength(Result, Length(utf8str));
Move(Pointer(utf8str)^, Pointer(Result)^, Length(utf8str));
end;
{$ENDIF}

Ansi 版本产生的堆分配超出了所需的数量。您可能会选择编写一个直接调用 WideCharToMultiByte() 的更高效的帮助程序。

在 Delphi 的 Unicode 版本中,如果由于某种原因您不想对 UTF-8 数据使用 TBytes,则可以使用 UTF8String 代替。这是一个特殊的 AnsiString,始终使用 CP_UTF8 代码页。然后你可以写:

var
utf8: UTF8String;
str: string;
....
utf8 := str;

编译器会在后台为您从 UTF-16 转换为 UTF-8。不过我不推荐这样做,因为它在移动平台或 Delphi 的 Ansi 版本中不受支持(UTF8String 自 Delphi 6 以来就已存在,但直到 Delphi 2009 为止它都不是真正的 UTF-8 字符串)。这就是为什么我建议您使用 TBytes 的原因之一。我的理念是,至少在 Unicode 时代,存在 native string 类型,任何其他编码都应保存在 TBytes 中。

关于delphi - 从 RawByteString 转换为字符串会自动调用 UTF8Decode 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24057569/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com