Ошибка записи строки UTF8 с помощью Datasnap - PullRequest
0 голосов
/ 18 февраля 2020

Недавно я перешел с D2007 на 10.3.3, и у меня возникла проблема ниже.

У меня есть TClientDataSet, который записывает данные в зашифрованном виде в формат .cds, сохраняя их в TStringField и используя метод .saveToFile . Проблема в том, что строка не соответствует записи в таблицу 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'. Вот как часы показывают зашифрованную строку:

enter image description here

А вот как строка фактически вставляется в столбец TStringField таблицы:

enter image description here

Поскольку строка в итоге неправильно вставляется в клиентский набор данных, когда я вызываю функцию дешифрования со строкой 'hrlgqivoy? |? ACE', я получаю неправильную исходная строка как 'engajamen3o1234' ...

Похоже, эта проблема связана с поддержкой UTF8 в новой версии Delphi, которой в старой версии не было.

Что мне нужно сделать для правильной записи строки в набор данных клиента и файл .cds, чтобы я вернул правильную расшифрованную строку?

1 Ответ

3 голосов
/ 18 февраля 2020

Я расширил ваш код в MRE, которое иллюстрирует вашу проблему и способы ее решения. В основном кажется, что строковое поле, которое вы используете для хранения зашифрованных данных, является TStringField, тогда как для работы с Unicode оно должно быть TWideStringField. Оба метода 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 никогда не используется.

...