Unicode字符串和TStringStream

Lut*_*hfi 7 delphi delphi-2010

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;
Run Code Online (Sandbox Code Playgroud)

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

Bar*_*lly 5

Unicode字符串不用于数据存储; 使用TBytes了点.TStringStream使用其关联的编码(Encoding属性)来编码传入的字符串WriteString,并解析用ReadStringDataString属性读出的字符串.


Lut*_*hfi 5

在阅读了这篇文章后(感谢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;
Run Code Online (Sandbox Code Playgroud)

最后一点:Unicode确实咬了!:d