2009-04-09 18 views
26

abbiamo avuto il seguente codice prima di Delphi 2009:Conversione TMemoryStream a 'String' nel 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> 

Come possiamo convertire questo codice per supportare Unicode ora con Delphi 2009?

risposta

62

Il codice che si possiede è inutilmente complesso, anche per le versioni Delphi più vecchie. Perché, dopo aver recuperato la versione di stringa di un flusso, la memoria del flusso dovrebbe essere riallocata, dopo tutto?

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

che funziona in tutte le versioni di Delphi, non solo Delphi 2009. Funziona quando il flusso è vuota senza alcun caso speciale. SetString è una funzione poco apprezzata.

Se il contenuto del vostro flusso non stanno cambiando a Unicode con il passaggio a Delphi 2009, allora si dovrebbe usare questa funzione invece:

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

Questo è equivalente al codice originale, ma salta i casi particolari .

+1

Ho fatto un sacco di memoria Delphi, ma non avevo sentito parlare di SetString, ho sempre usato SetLength (dest, length) e Move (src, @ (dest [1]), length); quale SetString fa (chiama _LStrFromPCharLen) –

+0

tipizzandolo su un PChar non dovrebbe creare alcun problema. –

+0

Il puntatore è compatibile con tutto. L'unica ragione per cui è necessario digitare il cast è se il compilatore ha avuto problemi con la risoluzione di sovraccarico. –

2

non ho ancora aggiornato, ma la mia comprensione è:

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

O forse si può refactoring del codice per utilizzare direttamente un TStringStream direttamente? Puoi usarlo al posto di TMemoryStream (hanno la stessa interfaccia) e puoi "convertirlo" in una stringa semplicemente chiamando myString: = myStringStream.DataString;

+1

In effetti, questa è stata la prima cosa che mi è venuta in mente. Perché non creare un TStringStream, caricare il memorystream al suo interno e restituire il datastring? –

12

Un modo "pulito" potrebbe essere:

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" non è richiesto come se si chiami "SS.CopyFrom (aStream, 0)", posizionerà la sorgente su 0 e otterrà le sue dimensioni. – jonjbar

+0

Ok, abbastanza giusto. –

+6

Devi stare attento con 'TStringStream' in D2009 +, dato che ora è 'TEncoding'-aware. 'CopyFrom()' copierà i byte grezzi della sorgente 'TStream' as-is, ma il getter delle proprietà' DataString' decodificherà quei byte usando qualunque cosa 'TEncoding' viene passata al costruttore 'TStringStream' (' TEncoding.Default' o 'TEncoding.UTF8' di default, IIRC). Quindi, se la codifica 'TStream' non corrisponde alla codifica' TStringStream', si otterranno perdita/danneggiamento dei dati. –

3

io uso:

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; 

E 'stato testato solo con Delphi XE7.

+0

'TBytes' è un array dinamico.Il primo parametro di 'ReadBuffer()' è un 'var' non tipizzato. È necessario dereferenziare' TBytes' per ottenere l'indirizzo di memoria corretto da passare a 'ReadBuffer()', ad es .: 'ReadBuffer (StringBytes [0], .. .) 'o più sicuro' ReadBuffer (PByte (StringBytes) ^, ...) 'quando' Size' è 0. –