【问题标题】:String losing data when assigning to TStringList分配给 TStringList 时字符串丢失数据
【发布时间】:2010-11-23 23:47:28
【问题描述】:

我有这个方法,

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;

ZCompressStr 来自http://www.base2ti.com/zlib.htm,第 121 行从 {$ifndef UNICODE} 到 {$ifdef UNICODE} 让它编译。

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

【问题讨论】:

  • 你为什么使用 TStringList?
  • 如果您描述了您想要完成的任务,您将更有可能获得有助于您实现目标的答案。例如, tstringlist 可能不是最好的工具(例如,字符串数组可能更好),当然分配给 tstringlist.text 可能会引起您的一些困惑(tstringlist.add 方法可能是您想要的)。不过,从您的问题中很难判断。
  • 我使用 TStringList 的原因是因为我想在压缩数据后将其保存到文件中——为此我通常一直使用 TStringList。看来这是一个不,我一直侥幸逃脱但 D2009 让我失望了。

标签: delphi string unicode delphi-2009 char


【解决方案1】:

您没有理由不得不更改 ZLibEx.pas 的第 121 行;它适用于所有版本的 Delphi,包括 Delphi 2009。UNICODE 符号应该为 Delphi 2009 定义,如果是,RawByteStringUnicodeString 的类型定义,和 UnicodeChar 都应该被跳过,因为它们已经是语言中的固有类型。

ZCompressStr 将生成一个可能包含不可打印字符的字符串,包括空字节。它将结果存储在 RawByteString 中,Delphi 对此进行了特殊处理。

TStringList 与 Delphi 2009 中的几乎所有其他内容一样,使用 Unicode。它的Text 属性是UnicodeString 类型。当您将 anyUnicodeString 值分配给UnicodeString 时,您将获得转换,就像来自MultiByteToWideStr API 函数一样。甚至RawByteString 也包含在该规则中。如果您没有为 RawByteString 分配特定于代码页的字符串值,那么它将具有代码页 0,即 CP_ACP,这是您系统的默认代码页。

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

As Smok1 mentioned, TStringList.Text 是一个属性。它有一个 setter 方法,可以将给定的字符串分成单独的行。当您读取该属性时,它会再次将所有这些行重新连接成一个字符串。在 设置 属性时,TStrings.SetTextStr(在 Classes.pas 中,如果您好奇的话)将在 #0#10#10、或#13。即空字符、换行和回车。当重新加入所有行时,它将使用其LineBreak 属性,该属性由全局sLineBreak 变量初始化。最后一个字符串之后也有一个换行符,所以每一行都以LineBreak结尾。因此,转换不一定是往返的。

所以,有两点可以借鉴:

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

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

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;

【讨论】:

  • 好点。事实上,TStringList 很聪明,所以无论使用 DOS 风格还是 unix 风格的行尾。
  • 谢谢 Rob,这是一个很棒的答案。您的第一段 - 是的,这很奇怪,但是您是否下载并尝试将 ZlibEx 用于他们的网站,在我更改该行之前它根本无法编译,另请参阅forums.embarcadero.com/thread.jspa?messageID=130234 答案的重置绝对是正确的。我一直在使用 TStrlingList 因为我想保存文件(TMemoryStream 可以这样做),但我想通过互联网上的表单发布这些数据。我会写下来并稍微修改一下我的问题。谢谢!
【解决方案2】:

这可能是转换 - TStringList.Text 属性是一个属性,而不是变量。您以一种有点危险的方式使用它,因为在 TStringList 中有一些 Text 处理。

【讨论】:

  • 我被贬低了。很高兴知道我的答案实际上有什么问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
相关资源
最近更新 更多