在Delphi 2009中将TMemoryStream转换为字符串

33

在 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个回答

73

你的代码过于复杂,即使是对于旧版的Delphi来说也是如此。毕竟,为什么获取流的字符串版本需要强制重新分配内存呢?

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

这适用于所有Delphi版本, 不仅限于Delphi 2009。 它在流为空时无需任何特殊情况即可工作。 SetString 是一个被低估的函数。

如果你的流内容在切换到Delphi 2009时没有转换为Unicode,则应改用此函数:

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

这与你原本的代码等价,但跳过了特殊情况。


1
我已经做了很多Delphi内存相关的工作,但我从未听说过SetString,总是使用SetLength(dest, length)和Move(src, @(dest[1]), length); SetString也可以执行这些操作(它调用_LStrFromPCharLen)。 - Davy Landman
将其转换为PChar类型不应该产生任何问题。 - Davy Landman
指针与所有内容兼容。唯一需要进行类型转换的原因是编译器在重载决策方面存在问题。 - Rob Kennedy
不错,Rob。我以前对SetString并没有太多的欣赏。 - Caleb Hattingh
3
第一个函数在D7上无法工作。编译器在第二个参数("memory")处出现“不兼容类型”的错误消息。因此,确实需要使用pchar类型转换。 - Gabriel
@RobKennedy 我在10.3中检查过了,运行良好。谢谢。 - M.Vakili

21

或者你可以重构你的代码直接使用TStringStream? 你可以使用它来替代TMemoryStream(它们有相同的接口),而且你可以通过简单地调用 myString := myStringStream.DataString; 来将其"转换"为字符串。


1
确实,这是我想到的第一件事。何不创建一个TStringStream,将MemoryStream加载到其中,并返回DataString? - The_Fox

17

一种更好的方法可能是:

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;

不需要使用"aStream.Position := 0",因为如果您调用"SS.CopyFrom(aStream, 0)",它将把源定位在0并为您获取其大小。 - jonjbar
9
在D2009及更高版本中,使用TStringStream时需要小心,因为它现在已经支持TEncoding编码。CopyFrom()方法将按原样复制源TStream的原始字节,但DataString属性getter方法将使用传递给TStringStream构造函数的TEncoding编码(默认为TEncoding.Default或TEncoding.UTF8)解码这些字节。因此,如果TStream的编码与TStringStream的编码不匹配,则可能会导致数据丢失/损坏。 - Remy Lebeau

6

我使用:

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测试。


3
TBytes 是一个动态数组。ReadBuffer() 的第一个参数是一个未指定类型的 var。你需要对 TBytes 进行解引用,以获取正确的内存地址传递给 ReadBuffer(),例如:ReadBuffer(StringBytes[0], ...) 或更安全的 ReadBuffer(PByte(StringBytes)^, ...)Size 为 0 时。 - Remy Lebeau

2

有一个因素叫做TStringStream,它将能够帮助您。. .您可以像这样加载另一个流的内容:

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

现在,您可以像这样进入String类型的系列:data-string属性包括系列...但是不要尝试使用大型对象,例如如果您将巨大文件加载到文件流中,然后将其复制到自己的字符串流中并尝试生成它,则会导致它占用大量内存!希望这有所帮助。

2

我还没有升级,但我的理解是:

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

1
你可以将其转换为正确大小的字符指针,然后简单地赋值:
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;

0
使用老牌的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; 
  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接