0

我一直在 Lazarus 从事一个项目,并决定暂时将其移至 Delphi XE(由于一些限制)。

简要概述正在发生的事情:

在运行时,我正在加载外部文件并将它们添加到流中。流属于从一个主要对象(TObject)派生的几个不同的类。这些类从主对象添加到 TList 中,基本上每个类都有自己的流属性,并且该类是主对象的子对象。

在这个主要对象中,我有一个保存和加载过程:

保存对象时,它还通过使用字符串流将所有来自其他类的流数据保存到文件中。此处的输出字符串必须是 base64 编码,因为我要保存到 XML。

打开文件时,想法是解码 base64 字符串并将其移回流中,就好像它是 base64 编码之前的原始文件一样。

在 Lazarus 中它可以工作,这里是重要的代码(注意,其中一些不是我写的)。

const
  Keys64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';

function Encode64String(S: string): string;
function Decode64String(S: string): string;
function Encode64StringToStream(const Input: TStream; var Output: string): Boolean;
procedure Decode64StringToStream(const Input: string; Output: TStream);
procedure StringToStream(Stream: TStream; const S: string);
function StreamToString(MS: TMemoryStream): string;

implementation

function Encode64String(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do
  begin
    x := Ord(s[i]);
    b := b * 256 + x;
    a := a + 8;
    while a >= 6 do
    begin
      a := a - 6;
      x := b div (1 shl a);
      b := b mod (1 shl a);
      Result := Result + Keys64[x + 1];
    end;
  end;
  if a > 0 then
  begin
    x := b shl (6 - a);
    Result := Result + Keys64[x + 1];
  end;
end;

function Decode64String(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do
  begin
    x := Pos(s[i], Keys64) - 1;
    if x >= 0 then
    begin
      b := b * 64 + x;
      a := a + 6;
      if a >= 8 then
      begin
        a := a - 8;
        x := b shr a;
        b := b mod (1 shl a);
        x := x mod 256;
        Result := Result + chr(x);
      end;
    end
    else
      Exit;
  end;
end;

function Encode64StringToStream(const Input: TStream; var Output: string): Boolean;
var
  MS: TMemoryStream;
begin
  Result := False;

  MS := TMemoryStream.Create;
  try
    Input.Seek(0, soFromBeginning);
    MS.CopyFrom(Input, Input.Size);
    MS.Seek(0, soFromBeginning);
    Output := Encode64String(StreamToString(MS));
  finally
    MS.Free;
  end;

  Result := True;
end;    

procedure Decode64StringToStream(const Input: string; Output: TStream);
var
  MS: TMemoryStream;
begin
  try
    MS := TMemoryStream.Create;
    try
      StringToStream(MS, Decode64String(Input));

      MS.Seek(0, soFromBeginning);
      Output.CopyFrom(MS, MS.Size);
      Output.Position := 0;
    finally
      MS.Free;
    end;

  except on E: Exception do
    raise Exception.Create('stream decode error - ' + E.Message);
  end;
end;

procedure StringToStream(Stream: TStream; const S: string);
begin
  Stream.Write(Pointer(S)^, Length(S));
end;

function StreamToString(MS: TMemoryStream): string;
begin
  SetString(Result, PChar(MS.Memory), MS.Size div SizeOf(Char));
end;

我 99% 确定这里的问题与 Unicode 相关。很遗憾,因为我相信 Lazarus/Freepascal 一直是 unicode 而不是 Delphi,因此使用了不同的字符串类型,这使得像我这样不太专业的用户几乎不可能解决!

老实说,我认为上面的所有代码都有些混乱,感觉就像我只是在猜测要将字符串更改为什么,而并不真正知道自己在做什么。

我的第一个想法是将所有内容从 更改StringAnsiString. 这几乎成功了一次,但是在尝试使用时,Decode64StringToStream我得到了零数据。其他时候,数据没有正确保存为 base64 编码格式,有时我什至遇到诸如 TStream.Seek 未实现之类的错误。

PS,我已经阅读了指南,并且有很多关于如何将旧的 Delphi 项目迁移到更新的 unicode 版本的白皮书等,老实说,我仍然对它一无所知。我认为替换stringAnsiString就足够了,但似乎不是。

任何提示、指示或一般建议或线索将不胜感激。

4

1 回答 1

4

我想你想做的是:

  1. 将 Unicode 字符串转换为 UTF-8 编码。这通常是 Unicode 文本最节省空间的格式。
  2. 使用 base64 对字符串进行编码。

然后解码你只需反转步骤。

代码如下所示:

function Encode(const Input: string): AnsiString;
var
  utf8: UTF8String;
begin
  utf8 := UTF8String(Input);
  Result := EncdDecd.EncodeBase64(PAnsiChar(utf8), Length(utf8));
end;

function Decode(const Input: AnsiString): string;
var
  bytes: TBytes;
  utf8: UTF8String;
begin
  bytes := EncdDecd.DecodeBase64(Input);
  SetLength(utf8, Length(bytes));
  Move(Pointer(bytes)^, Pointer(utf8)^, Length(bytes));
  Result := string(utf8);
end;
于 2013-10-01T10:46:03.223 回答