Строка, теряющая данные при присваивании TStringList - PullRequest
2 голосов
/ 29 августа 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;

ZCompressStr от http://www.base2ti.com/zlib.htm, строка 121 изменена с {$ ifndef UNICODE} в {$ ifdef UNICODE} чтобы он скомпилировался.

В любом случае, я могу вызвать ZDecompressStr, если я использую переменную fVar, однако, как только я перемещаю ее в список строк или в заметку, кажется, что эти 6 байтов данных теряются .... Если я пытаюсь использовать ZDecompressStr на .text var сбой с ошибкой буфера.

Ответы [ 2 ]

14 голосов
/ 29 августа 2009

Нет причины, по которой вам пришлось бы менять строку 121 ZLibEx.pas ; это верно для всех версий Delphi, включая Delphi 2009. Символ UNICODE должен only быть определен для Delphi 2009, а когда это так, определения типов для RawByteString, UnicodeString и UnicodeChar все должны быть пропущены, потому что они уже являются внутренними типами в языке.

ZCompressStr создаст строку, которая может содержать непечатаемые символы, включая нулевые байты. Он сохраняет свой результат в RawByteString, который Delphi обрабатывает специально.

TStringList, как и все остальное в Delphi 2009, использует Unicode. Свойство Text имеет тип UnicodeString. Когда вы присваиваете любое отличное от UnicodeString значение UnicodeString, вы получаете преобразование из функции API MultiByteToWideStr. Даже RawByteString включено в это правило. Если вы не присвоили строковое значение для кодовой страницы RawByteString, тогда у него будет кодовая страница 0, которая является CP_ACP, кодовой страницей по умолчанию для вашей системы.

Если строка на самом деле не содержит символов, закодированных в соответствии с системной кодовой страницей, то любое преобразование вызывает проблемы: мусор внутри, мусор вне. В частности, нет гарантии, что вы получите такое же количество символов.

Как упоминал Smok1 , TStringList.Text является собственностью. У него есть метод установки, который разбивает данную строку на отдельные строки. Когда вы читаете свойство, оно снова объединяет все эти строки в одну строку. В то время как устанавливает свойство, TStrings.SetTextStrClasses.pas , если вам интересно) будет разбивать строку в любом случае #0, #10 или #13. То есть нулевые символы, переводы строки и возврат каретки. При повторном соединении всех строк он будет использовать свое свойство LineBreak, которое инициализируется глобальной переменной sLineBreak. Разрыв строки также ставится после последней строки, поэтому каждая строка заканчивается LineBreak. Таким образом, преобразование не обязательно в оба конца.

Итак, из этого можно извлечь две вещи:

  1. Не обрабатывать сжатые данные как текст.
  2. Не используйте TStrings потомков для хранения вещей, которые вы не хотите обрабатывать несколькими строками.

Еще один хороший совет: не используйте string как общий тип хранения данных. Используйте его только для реального текста. Для хранения произвольных двоичных данных предпочтите TBytes или 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;
2 голосов
/ 29 августа 2009

Это может быть преобразование - свойство TStringList.Text является свойством, а не переменной. Вы используете его немного опасным образом, поскольку в TStringList происходит некоторая обработка текста.

...