【问题标题】:Error on recording UTF8 string with Datasnap使用 Datasnap 记录 UTF8 字符串时出错
【发布时间】:2020-06-01 22:28:42
【问题描述】:

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

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

这是我的加密功能:

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' 调用这个函数。 以下是手表显示加密字符串的方式:

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

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

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

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

【问题讨论】:

  • 在不知道数据实际应该是什么的情况下,Watch 窗口显示的内容并没有真正的帮助。您应该添加一个代码示例,显示数据被插入 CDS 并从中读取。也许您的意思是使用 Rio 无法正确读取使用 D2007 保存到磁盘的 CDS 数据?
  • 数据应该和第一个手表显示的一样;第二个是替换 #$0080 为 ?
  • 问题只出在 Rio 上:它错误地保存了数据。
  • 当您执行 CDS.SaveToFile 时,您使用的是什么 TPacketDataFormat?我仍然认为您需要一个演示问题的代码示例,顺便说一句。
  • 好的,我刚刚用我正在使用的实际代码更新了问题。

标签: delphi delphi-2007 delphi-10.3-rio


【解决方案1】:

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

代码使用条件定义 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 的值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多