Строка Unicode и TStringStream - PullRequest
       0

Строка Unicode и TStringStream

7 голосов
/ 11 октября 2010

Delphi 2009 и выше использует строки Юникода в качестве типа строк по умолчанию. Насколько я понимаю, символ unicode на самом деле представляет собой 16-битное значение или 2 байта (примечание: я понимаю, что существует возможность использования 3-х или 4-х байтовых символов, но давайте рассмотрим наиболее обычный случай). Однако я обнаружил, что 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 в качестве посредника при чтении / записи буфера и использовать CopyFrom из TStringStream для чтения содержимого этого TMemoryStream с таким же ошибочным эффектом.

Ответы [ 2 ]

5 голосов
/ 11 октября 2010

После прочтения этого поста (и благодаря Сергу, который предоставил ответ на этот вопрос) и ответа Барри Келли, я нашел проблему. TStringStream фактически использует ASCII / ansistring кодировку по умолчанию. Таким образом, даже если ваш тип строки по умолчанию - Юникод, если вы специально не сообщите об этом, он не будет использовать кодировку Юникод. Лично я думаю, что это странно. Может быть, для облегчения конвертации старых кодов.

Таким образом, вы должны специально установить кодировку TStringStream в TEncoding.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

5 голосов
/ 11 октября 2010

Строки Unicode не предназначены для хранения данных; используйте TBytes для этого. TStringStream использует ассоциированное кодирование (свойство Encoding) для кодирования строк, передаваемых с WriteString, и декодирования строк, считываемых с помощью ReadString или DataString.

...