2009-04-09 8 views
26

Nous avons eu le code suivant avant Delphi 2009:Conversion TMemoryStream à 'String' dans Delphi 2009

<span style="font: 10pt Courier New;"><span class="pas1-reservedword">function</span><span class="pas1-space"> </span><span class="pas1-identifier">MemoryStreamToString(M:</span><span class="pas1-space"> </span><span class="pas1-identifier">TMemoryStream):</span><span class="pas1-space"> </span><span class="pas1-reservedword">String</span><span class="pas1-symbol">; 
</span><span class="pas1-reservedword">var 
</span><span class="pas1-space"> </span><span class="pas1-identifier">NewCapacity:</span><span class="pas1-space"> </span><span class="pas1-identifier">Longint; 
</span><span class="pas1-reservedword">begin 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">if</span><span class="pas1-space"> </span><span class="pas1-symbol">(M.Size</span><span class="pas1-space"> </span><span class="pas1-symbol">=</span><span class="pas1-space"> </span><span class="pas1-number">0)</span><span class="pas1-space"> </span><span class="pas1-reservedword">or</span><span class="pas1-space"> </span><span class="pas1-symbol">(M.Memory</span><span class="pas1-space"> </span><span class="pas1-symbol">=</span><span class="pas1-space"> </span><span class="pas1-reservedword">nil</span><span class="pas1-symbol">)</span><span class="pas1-space"> </span><span class="pas1-reservedword">then 
</span><span class="pas1-space"> </span><span class="pas1-identifier">Result:=</span><span class="pas1-space"> </span><span class="pas1-string">'' 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">else 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">begin 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">if</span><span class="pas1-space"> </span><span class="pas1-identifier">TMemoryStreamProtected(M).Capacity</span><span class="pas1-space"> </span><span class="pas1-symbol">=</span><span class="pas1-space"> </span><span class="pas1-identifier">M.Size</span><span class="pas1-space"> </span><span class="pas1-reservedword">then 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">begin 
</span><span class="pas1-space">  </span><span class="pas1-identifier">NewCapacity:=</span><span class="pas1-space"> </span><span class="pas1-identifier">M.Size+1; 
</span><span class="pas1-space">  </span><span class="pas1-identifier">TMemoryStreamProtected(M).Realloc(NewCapacity); 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">end</span><span class="pas1-symbol">; 
</span><span class="pas1-space"> </span><span class="pas1-identifier">NullString(M.Memory^)[M.Size]:=</span><span class="pas1-space"> </span><span class="pas1-character">#0; 
</span><span class="pas1-space"> </span><span class="pas1-identifier">Result:=</span><span class="pas1-space"> </span><span class="pas1-identifier">StrPas(M.Memory); 
</span><span class="pas1-space"> </span><span class="pas1-reservedword">end</span><span class="pas1-symbol">; 
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">; 
</span></span> 

Comment pourrait-on convertir ce code pour soutenir maintenant Unicode avec Delphi 2009?

Répondre

62

Le code que vous avez est inutilement complexe, même pour les anciennes versions de Delphi. Pourquoi la récupération de la version chaîne d'un flux force-t-elle la réallocation de la mémoire du flux, après tout?

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

qui fonctionne dans toutes les versions Delphi, pas seulement Delphi 2009. Il fonctionne lorsque le flux est vide sans aucun cas particulier. SetString est une fonction sous-estimée.

Si le contenu de votre flux ne changent pas d'Unicode avec votre commutateur à Delphi 2009, vous devez utiliser cette fonction au lieu:

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

C'est équivalent à votre code d'origine, mais ignore les cas particuliers .

+1

J'ai fait beaucoup de choses sur la mémoire Delphi, mais je n'avais pas entendu parler de SetString, toujours utilisé SetLength (dest, length) et Move (src, @ (dest [1]), length); ce que SetString fait aussi (il appelle _LStrFromPCharLen) –

+0

le typecasting à un PChar ne devrait pas créer de problèmes. –

+0

Pointer est compatible avec tout. La seule raison pour laquelle vous auriez besoin de lancer un cast est si le compilateur avait des problèmes avec la résolution de surcharge. –

2

Je n'ai pas mis à jour encore, mais je crois comprendre:

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

Ou peut-être vous pouvez factoriser votre code pour utiliser directement un TStringStream directement? Vous pouvez l'utiliser à la place de TMemoryStream (ils ont la même interface) et vous pouvez le 'convertir' en une chaîne en appelant simplement myString: = myStringStream.DataString;

+1

En effet, c'était la première chose qui me venait à l'esprit. Pourquoi ne pas créer un TStringStream, y charger le memorystream et retourner le datastring? –

12

Un "plus propre" façon peut-être:

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; 
+0

"aStream.Position: = 0" n'est pas requis comme si vous appelez "SS.CopyFrom (aStream, 0)", il positionnera la source à 0 et obtiendra sa taille pour vous. – jonjbar

+0

D'accord, c'est juste. –

+6

Vous devez faire attention à 'TStringStream' dans D2009 +, car il est maintenant' TEncoding'-conscient. 'CopyFrom()' copiera les octets bruts de la source 'TStream' tel quel, mais le getter de propriété' DataString' décodera ces octets en utilisant tout ce que 'TEncoding' est passé au constructeur' TStringStream' ('TEncoding.Default' ou 'TEncoding.UTF8' par défaut, IIRC). Donc, si le codage 'TStream' ne correspond pas au codage' TStringStream', vous risquez de perdre/de corrompre les données. –

3

J'utilise:

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; 

Il a été testé avec Delphi XE7 seulement.

+0

'TBytes' est un tableau dynamique.Le premier paramètre de 'ReadBuffer()' est un 'var' non typé Vous devez déréférencer le' TBytes' pour obtenir l'adresse mémoire correcte à passer à ReadBuffer() ', par exemple:' ReadBuffer (StringBytes [0], .. .) 'ou plus sûr' ReadBuffer (PByte (StringBytes) ^, ...) 'quand' Size' est 0. –

Questions connexes