Converting TMemoryStream to 'String' in Delphi 2009
-
06-09-2019 - |
Question
We had the following code prior to 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;
How might we convert this code to support Unicode now with Delphi 2009?
Solution
The code you have is unnecessarily complex, even for older Delphi versions. Why should fetching the string version of a stream force the stream's memory to be reallocated, after all?
function MemoryStreamToString(M: TMemoryStream): string;
begin
SetString(Result, PChar(M.Memory), M.Size div SizeOf(Char));
end;
That works in all Delphi versions, not just Delphi 2009. It works when the stream is empty without any special case. SetString
is an under-appreciated function.
If the contents of your stream aren't changing to Unicode with your switch to Delphi 2009, then you should use this function instead:
function MemoryStreamToString(M: TMemoryStream): AnsiString;
begin
SetString(Result, PAnsiChar(M.Memory), M.Size);
end;
That's equivalent to your original code, but skips the special cases.
OTHER TIPS
Or perhaps you can refactor your code to use directly a TStringStream directly? You can use it instead of TMemoryStream (they have the same interface) and you can 'convert' it to a string by simply calling myString := myStringStream.DataString;
A "cleaner" way might be:
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;
I use:
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;
It has been tested with Delphi XE7 only.
I have not upgraded yet, but my understanding is:
NewCapacity := (M.Size + 1) * SizeOf(Char);
There's a factor called TStringStream
that will be able to assist you. . .you can load the contents of another flow like that:
var StringStream: TStringStream;
begin StringStream := TStringStream.Create('');
StringStream.CopyFrom(OtherStream, OtherStream.Size);
end;
You can now get into the series for a String kind such as this: The data-string property comprises the series... but do not try so with large objects such as in the event that you load a huge file to some filestream then copy this to your own stringstream and make an effort to produce it cause it arranges a lot of memory!
Hope that helps
You can cast it into the right sized character pointer and just simple assign it:
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;