使用 Datasnap 记录 UTF8 字符串时出错

Error on recording UTF8 string with Datasnap

最近我从 D2007 迁移到 10.3.3,但遇到以下问题。

我有一个 TClientDataSet,它通过将加密字符串数据存储在 TStringField 中并使用 .saveToFile 方法将数据记录为 .cds 格式。问题是,字符串没有被正确记录到 CDS table.

这里是我的加密函数:

function encrypt(const ent: string): string;
var m, i, k : integer;
r, s : string;
begin
m := 3;
r := '';
for I := 1 to Length(ent) do
  begin
  k := ord(ent[i]);
  s := chr(k+m);
  r := r + s;
  inc(m);
  end;
result := r;
end;

这是我的解密函数:

function decrypt(const ent: string): string;
var m, i,j,K : integer;
r, s : string;
begin
m := 3;
r := '';
s := ent;
for I := 1 to length(s) do
  begin
  if ord(s[i]) < 68 then j := 1 else
    j := -1;
  k := ord(s[i]);
  r := r + chr(k-m);
  inc(m);
  end;
result := r;
end;

现在我想用字符串 'engajamento1234' 调用这个函数。 这是手表显示加密字符串的方式:

下面是将字符串实际插入到 table 的 TStringField 列中的方式:

因为字符串最终被错误地插入客户端数据集,当我用 'hrlgqivoy?|?ACE' 字符串调用解密函数时,我得到不正确的原始字符串 'engajamen3o1234'...

这个问题似乎与新Delphi版本对UTF8的支持有关,旧版本没有。

我需要做什么才能将字符串正确记录到客户端数据集和 .cds 文件中,这样我才能得到正确的解密字符串?

我已将您的代码扩展为 MRE,它说明了您遇到的问题以及如何解决 解决这个问题。基本上,您用来存储加密数据的字符串字段似乎 是一个 TStringField,而它需要是一个 TWideStringField 才能使用 Unicode。 TStringField的 SetAsString 和 GetAsString 方法都使用将数据视为 ANSIString 的调用(这是 Delphi 字符串的类型,早于 Unicode 的引入)。

该代码使用条件定义 UseWideString,它确定 CDS 字段是 ftString 类型(如在 D2007 中使用的那样)还是 ftWideString,后者是 Unicode 等价物。您应该发现,在 UseWideString 定义生效的情况下,代码执行时没有错误, 这意味着解密后的字符串与原始输入相同。如果你注释掉 UseWideString 定义,

Assert(sInput = sDecrypted);

失败。

代码

program EncryptTest;

{$APPTYPE CONSOLE}

uses
  SysUtils, db, dbclient;

function encrypt(const ent: string): string;
var m, i, k : integer;
r, s : string;
begin
m := 3;
r := '';
for I := 1 to Length(ent) do
  begin
  k := ord(ent[i]);
  s := chr(k+m);
  r := r + s;
  inc(m);
  end;
result := r;
end;

function decrypt(const ent: string): string;
var m, i,j,K : integer;
r, s : string;
begin
m := 3;
r := '';
s := ent;
for I := 1 to length(s) do
  begin
  if ord(s[i]) < 68 then j := 1 else
    j := -1;
  k := ord(s[i]);
  r := r + chr(k-m);
  inc(m);
  end;
result := r;
end;

var
  sInput,
  sEncrypted,
  sDecrypted : String;
  CDS : TClientDataSet;
  Field : TField;

begin
  CDS := TClientDataSet.Create(Nil);

{.$define UseWideString}
{$ifdef UseWideString}
  Field := TWideStringfield.Create(Nil);
{$else}
  Field := TStringfield.Create(Nil);
{$endif}
  Field.FieldKind := fkData;
  Field.Size := 80;
  Field.FieldName := 'Something';
  Field.DataSet := CDS;

  CDS.CreateDataSet;
  CDS.Append;
  CDS.Edit;

  sInput := 'engajamento1234';
  sEncrypted := encrypt(sInput);
  Field.AsString := sEncrypted;
  sDecrypted := decrypt(Field.AsString);
  Assert(sInput = sDecrypted);

  CDS.Cancel;
  Field.Free;
  CDS.Free;

end.

顺便说一句,在您的 decrypt 函数中,

if ord(s[i]) < 68 then j := 1 else
  j := -1;

是多余的,因为从未使用过 j 的值。