分配给TStringList时丢失数据的字符串

Wiz*_*ard 2 delphi string unicode char delphi-2009

我有这个方法,

var
s : TStringList;
fVar : string;
begin
s := TStringList.Create;
fVar := ZCompressStr('text');

ShowMessage( IntToStr(length(fVar) * SizeOf(Char)) );
//24

s.text := fVar;  

ShowMessage( IntToStr( length(s.text) * SizeOf(Char)) );
//18
end;
Run Code Online (Sandbox Code Playgroud)

ZCompressStr来自http://www.base2ti.com/zlib.htm,第121行从{$ ifndef UNICODE}更改为{$ ifdef UNICODE}以进行编译.

无论如何,如果我使用fVar变量,我可以调用ZDecompressStr,但是一旦我将它移动到字符串列表或备忘录,它似乎松开了那6个字节的数据....如果我尝试在s.text var上使用ZDecompressStr它因缓冲区错误而失败.

Rob*_*edy 14

没有理由你必须改变ZLibEx.pas的第121 ; 这是德尔福的所有版本,包括德尔福2009年正确的UNICODE符号应针对德尔福2009年被定义,当它是,该类型的定义RawByteString,UnicodeString以及UnicodeChar都应该跳过,因为他们在语言已经固有的类型.

ZCompressStr将生成一个可能包含不可打印字符的字符串,包括空字节.它将其结果存储在RawByteStringDelphi专门处理的中.

TStringList就像Delphi 2009中的其他所有内容一样,它使用Unicode.它的Text属性是类型UnicodeString.当您分配任何UnicodeString值时UnicodeString,您将获得转换,如MultiByteToWideStrAPI函数.甚至RawByteString包含在该规则中.如果您尚未为a指定特定于代码页的字符串值RawByteString,则它将具有代码页0,即CP_ACP系统的默认代码页.

如果字符串实际上不包含根据系统代码页编码的字符,那么任何转换都会产生麻烦:垃圾输入,垃圾输出.特别是,不能保证您将获得相同数量的字符.

正如Smok1所说,TStringList.Text是一个财产.它有一个setter方法,将给定的字符串拆分为单独的行.当您读取属性时,它会再次将所有这些行重新连接成一个字符串.当设置该属性,TStrings.SetTextStr(在Classes.pas,如果你好奇)将在出现的任何分裂线#0,#10#13.也就是说,空字符,换行符和回车符.当重新连接所有行时,它将使用其LineBreak属性,该属性使用全局sLineBreak变量初始化.换行符也放在最后一个字符串之后,所以每一行都以LineBreak.因此,转换不一定是往返.

因此,有两点需要从中学习:

  1. 不要将压缩数据视为文本.
  2. 不要使用TStrings后代来保存您不想处理多个字符串的内容.

另一个好建议:不要string用作通用数据存储类型.仅用于实际文本.对于任意二进制数据的存储,首选TBytes或a TMemoryStream.使用您的示例,您可以压缩这样的字符串:

var
  ss: TStream;
  ms: TMemoryStream;
begin
  ss := TStringStream.Create('text');
  try
    ms := TMemoryStream.Create;
    try
      ShowMessage(IntToStr(ss.Size));
      ZCompressStream(ss, ms);
      ShowMessage(IntToStr(ms.Size));
    finally
      ms.Free;
    end;
  finally
    ss.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)