加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-15 09:32:38 所属栏目:大数据 来源:网络整理
导读:我想将任意二进制数据作为BLOB存储到SQlite数据库中. 使用此函数将数据添加为值: procedure TSQLiteDatabase.AddParamText(name: string; value: string); 现在我想将WideString转换为其UTF8表示,因此可以将其存储到数据库中.在调用UTF8Encode并将结果存储
我想将任意二进制数据作为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:

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,而不是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中.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读