【问题标题】:Unicode string and TStringStreamUnicode 字符串和 TStringStream
【发布时间】:2010-10-11 13:52:49
【问题描述】:

Delphi 2009 及更高版本使用 unicode 字符串作为其默认字符串类型。据我了解,unicode char 实际上是 16 位值或 2 个字节(注意:我知道可能有 3 或 4 个字节的 char,但让我们考虑最常见的情况)。但是我发现 TStringStream 操作这些字符串不是很可靠。例如,TStringStream.Size 属性返回字符串的长度,而我认为它应该返回包含字符串的字节数。好吧,你可以自己调整它,但最让我困惑的是:TStringStream 不能可靠地读取或写入缓冲区。

请检查以下代码(这是一个 DUnit 测试,总是失败)。请告诉我问题出在哪里(我在测试代码时使用的是 D2010)。

procedure TestTCPackage.TestStringStream;
const
  cCount = 10;
  cOrdMaxChar = Ord(High(Char));
var
  B: Pointer;
  SW, SR: TStringStream;
  T: string;
  i, j, k : Integer;
  vStrings: array [0..cCount-1] of string;
begin
  RandSeed := GetTickCount;
  for i := 0 to cCount - 1 do
  begin
    j := Random(100) + 1;
    SetLength(vStrings[i], j);
    for k := 1 to j do
      // fill string with random char (but no #0)
      vStrings[i][k] := Char(Random(cOrdMaxChar-1) + 1);
  end;

  for i := 0 to cCount - 1 do
  begin
    SW := TStringStream.Create(vStrings[i]);
    try
      GetMem(B, SW.Size * SizeOf(Char));
      try
        SW.Read(B^, SW.Size * SizeOf(Char));

        SR := TStringStream.Create;
        try
          SR.Write(B^, SW.Size * SizeOf(Char));
          SR.Position := 0;

          // check the string in the TStringStream with original value
          Check(SR.DataString = vStrings[i]);
        finally
          SR.Free;
        end;
      finally
        FreeMem(B);
      end;
    finally
      SW.Free;
    end;
  end;
end;

注意:我已经尝试使用 TMemoryStream 的实例作为读取/写入缓冲区的中介,并使用 TStringStream 的 CopyFrom 读取该 TMemoryStream 的内容,但效果相同。

【问题讨论】:

    标签: delphi delphi-2010


    【解决方案1】:

    Unicode 字符串不用于数据存储;为此使用TBytesTStringStream 使用其关联的编码(Encoding 属性)对使用WriteString 传入的字符串进行编码,并对使用ReadStringDataString 属性读出的字符串进行解码。

    【讨论】:

    • 其实代码不是关于数据存储的,但是看了你的回答和另一篇帖子后,我发现了问题所在。默认情况下,TStringStream 使用的是 ASCII/ansistring。谢谢!
    【解决方案2】:

    在阅读this post(感谢 Serg 提供了该问题的答案)和 Barry Kelly 的回答后,我发现了问题所在。 TStringStream 实际上默认使用 ASCII/ansistring 编码。因此,即使您的默认字符串类型是 unicode,除非您特别告诉它,否则它不会使用 unicode 编码。个人觉得很奇怪。也许是为了更容易转换旧代码。

    因此,您必须专门将 TStringStream 的编码设置为 TEncoding.Unicode 以正确操作 unicode 字符串。

    这是我通过 DUnit 测试的修改代码:

    procedure TestTCPackage.TestStringStream;
    const
      cCount = 10;
      cOrdMaxChar = Ord(High(Char));
    var
      B: Pointer;
      SW, SR: TStringStream;
      i, j, k : Integer;
      vStrings: array [0..cCount-1] of string;
    begin
      RandSeed := GetTickCount;
      for i := 0 to cCount - 1 do
      begin
        j := Random(100) + 1;
        SetLength(vStrings[i], j);
        for k := 1 to j do
          // fill string with random char (but no #0)
          vStrings[i][k] := Char(Random(cOrdMaxChar-1) + 1);
      end;
    
      for i := 0 to cCount - 1 do
      begin
        SW := TStringStream.Create(vStrings[i], ***TEncoding.Unicode***);
        try
          GetMem(B, SW.Size);
          try
            SW.ReadBuffer(B^, SW.Size);
    
            SR := TStringStream.Create('', ***TEncoding.Unicode***);
            try
              SR.WriteBuffer(B^, SW.Size);
              SR.Position := 0;
    
              // check the string in the TStringStream with original value
              Check(SR.DataString = vStrings[i]);
            finally
              SR.Free;
            end;
          finally
            FreeMem(B);
          end;
        finally
          SW.Free;
        end;
      end;
    end;
    

    最后一点:Unicode 确实会咬人! :D

    【讨论】:

      猜你喜欢
      • 2014-11-25
      • 2021-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-14
      • 2017-11-19
      • 2012-07-26
      相关资源
      最近更新 更多