Преобразование TMemoryStream в 'String' в Delphi 2009

У нас был следующий код до Delphi 2009:

function MemoryStreamToString(M : TMemoryStream): String;
var
    NewCapacity: Longint;
begin
    if (M.Size = > 0) or (M.Memory = nil) then
       Result:= '' 
    else
    begin
       if TMemoryStreamProtected(M).Capacity = M.Size then
       begin
           NewCapacity:= M.Size+1;
           TMemoryStreamProtected(M).Realloc(NewCapacity);
       end;
       NullString(M.Memory^)[M.Size]:= #0;
       Result:= StrPas(M.Memory);
    end;
end;

Как мы можем преобразовать этот код для поддержки Unicode сейчас с Delphi 2009?

8 ответов

Решение

Код, который у вас есть, излишне сложен, даже для более старых версий Delphi. Почему выборка строковой версии потока заставляет его перераспределять память, в конце концов?

function MemoryStreamToString(M: TMemoryStream): string;
begin
  SetString(Result, PChar(M.Memory), M.Size div SizeOf(Char));
end;

Это работает во всех версиях Delphi, не только в Delphi 2009. Это работает, когда поток пуст без какого-либо особого случая. SetString недооцененная функция.

Если содержимое вашего потока не меняется на Unicode с вашим переключением на Delphi 2009, тогда вы должны использовать эту функцию вместо:

function MemoryStreamToString(M: TMemoryStream): AnsiString;
begin
  SetString(Result, PAnsiChar(M.Memory), M.Size);
end;

Это эквивалентно вашему исходному коду, но пропускает особые случаи.

Или, может быть, вы можете реорганизовать свой код для непосредственного использования TStringStream? Вы можете использовать его вместо TMemoryStream (они имеют одинаковый интерфейс), и вы можете "преобразовать" его в строку, просто вызвав myString:= myStringStream.DataString;

"Чистым" способом может быть:

function StreamToString(aStream: TStream): string;
var
  SS: TStringStream;
begin
  if aStream <> nil then
  begin
    SS := TStringStream.Create('');
    try
      SS.CopyFrom(aStream, 0);  // No need to position at 0 nor provide size
      Result := SS.DataString;
    finally
      SS.Free;
    end;
  end else
  begin
    Result := '';
  end;
end;

Я использую:

function StreamToString(const Stream: TStream; const Encoding: TEncoding): string;
var
  StringBytes: TBytes;
begin
  Stream.Position := 0;
  SetLength(StringBytes, Stream.Size);
  Stream.ReadBuffer(StringBytes, Stream.Size);
  Result := Encoding.GetString(StringBytes);
end;

Он был протестирован только с Delphi XE7.

Есть фактор под названием TStringStream это сможет помочь вам.,.вы можете загрузить содержимое другого потока следующим образом:

var StringStream: TStringStream; 
begin StringStream := TStringStream.Create(''); 
StringStream.CopyFrom(OtherStream, OtherStream.Size); 
end;

Теперь вы можете войти в серию для типа String, такого как это: свойство data-string включает серию... но не пытайтесь делать это с большими объектами, например, если вы загружаете огромный файл в некоторый файловый поток, затем копируете это к вашему собственному потоку строк и приложите усилия, чтобы произвести это, потому что это организует много памяти!

надеюсь, это поможет

Я еще не обновился, но мое понимание:

NewCapacity := (M.Size + 1) * SizeOf(Char);

Вы можете преобразовать его в указатель символа нужного размера и просто назначить его:

procedure getMemoryStreamAsString( aMS_ : TMemoryStream );
var
  ws : widestring; // in newer Delphi it can be string
  ans : ansistring;
begin
  ws := pwidechar( aMS_.memory );
  // OR
  ans := pansichar( aMS_.memory );
end;

используя старый добрый StringList

      function StreamToString (Stream : TStream; Encoding : TEncoding) : string;
begin
  var List := TStringList.Create;
  try
    List.LoadFromStream (Stream, Encoding);
    Result := List.Text;
  finally
    List.Free;
  end; 
end; 
  
Другие вопросы по тегам