delphi – 从RawByteString转换为字符串会自动调用UTF8Decode吗
我想将任意二进制数据作为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值传递给函数(作为字符串或AnsiString),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使用其指定的CP-UTF8代码页转换为UTF-16. 如果您实际上将blob数据直接写入数据库,您会发现它可能是也可能不是UTF-8编码,具体取决于您编写它的方式.但你的做法是错误的;在这种情况下,使用RawByteString是不正确的. RawByteString仅用作过程参数.不要将它用作局部变量.这是你问题的根源.从documentation:
对于Unicode版本的Delphi,而不是RawByteString,我建议您使用TBytes来保存您的UTF-8数据,并使用TEncoding对其进行编码: var utf8: TBytes; str: string; ... str := ...; utf8 := TEncoding.UTF8.GetBytes(str); 您正在寻找一种在传递时不执行隐式文本编码的数据类型,而TBytes就是该类型. 对于Ansi的Ansi版本,您可以完全像您一样使用AnsiString,WideString和UTF8Encode. 但就个人而言,我建议一致地使用TBytes来获取您的UTF-8数据.因此,如果您需要一个支持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(). 在Unicode版本的Delphi中,如果由于某种原因您不想将TBytes用于UTF-8数据,则可以使用UTF8String.这是一个特殊的AnsiString,它总是使用CP_UTF8代码页.然后你可以写: var utf8: UTF8String; str: string; .... utf8 := str; 并且编译器将在幕后为您转换为UTF-16到UTF-8.我不推荐这个,因为它不支持移动平台,也不支持Ansi的Ansi版本(自Delphi 6以来已经存在UTF8String,但在Delphi 2009之前它不是真正的UTF-8字符串).也就是说,除其他原因外,为什么我建议您使用TBytes.我的理念是,至少在Unicode时代,存在本机字符串类型,并且任何其他编码都应该保存在TBytes中. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |