(Wide) String - memorizzazione in TFileStream, Delphi 7. Qual è il modo più veloce?
-
20-09-2019 - |
Domanda
Sto usando Delphi7 (non Unicode VCL), ho bisogno di memorizzare un sacco di WideStrings all'interno di un TFileStream. Non posso usare TStringStream come i (larghezza) le stringhe sono mescolati con dati binari, il formato è proiettata per velocizzare il caricamento e la scrittura dei dati ... Comunque credo che attuale modo sto caricando / scrittura delle stringhe potrebbe essere un collo di bottiglia del mio codice ...
Al momento sto scrivendo lunghezza di una stringa, poi scriverlo char da char ... durante il caricamento, prima sto caricando la lunghezza, poi caricando char da char ...
Quindi, qual è il modo più veloce per salvare e caricare WideString a TFileStream?
Grazie in anticipo
Soluzione
Invece di leggere e scrivere un carattere alla volta, leggere e scrivere tutti in una volta:
procedure WriteWideString(const ws: WideString; stream: TStream);
var
nChars: LongInt;
begin
nChars := Length(ws);
stream.WriteBuffer(nChars, SizeOf(nChars);
if nChars > 0 then
stream.WriteBuffer(ws[1], nChars * SizeOf(ws[1]));
end;
function ReadWideString(stream: TStream): WideString;
var
nChars: LongInt;
begin
stream.ReadBuffer(nChars, SizeOf(nChars));
SetLength(Result, nChars);
if nChars > 0 then
stream.ReadBuffer(Result[1], nChars * SizeOf(Result[1]));
end;
Ora, tecnicamente, dal momento che è un WideString
BSTR
di Windows, può contenere un dispari numero di byte. La funzione Length
legge il numero di byte e divide per due, quindi è possibile (anche se non probabile) che il precedente codice interrompe l'ultimo byte. Si potrebbe utilizzare questo codice invece:
procedure WriteWideString(const ws: WideString; stream: TStream);
var
nBytes: LongInt;
begin
nBytes := SysStringByteLen(Pointer(ws));
stream.WriteBuffer(nBytes, SizeOf(nBytes));
if nBytes > 0 then
stream.WriteBuffer(Pointer(ws)^, nBytes);
end;
function ReadWideString(stream: TStream): WideString;
var
nBytes: LongInt;
buffer: PAnsiChar;
begin
stream.ReadBuffer(nBytes, SizeOf(nBytes));
if nBytes > 0 then begin
GetMem(buffer, nBytes);
try
stream.ReadBuffer(buffer^, nBytes);
Result := SysAllocStringByteLen(buffer, nBytes)
finally
FreeMem(buffer);
end;
end else
Result := '';
end;
Mghie di rispondere , hanno sostituito i miei Read
e Write
chiamate con ReadBuffer
e WriteBuffer
. Quest'ultimo solleverà eccezioni, se non sono in grado di leggere o scrivere il numero richiesto di byte.
Altri suggerimenti
Non c'è niente di speciale in ampi archi, di leggere e scrivere loro il più velocemente possibile è necessario leggere e scrivere il più possibile in un colpo solo:
procedure TForm1.Button1Click(Sender: TObject);
var
Str: TStream;
W, W2: WideString;
L: integer;
begin
W := 'foo bar baz';
Str := TFileStream.Create('test.bin', fmCreate);
try
// write WideString
L := Length(W);
Str.WriteBuffer(L, SizeOf(integer));
if L > 0 then
Str.WriteBuffer(W[1], L * SizeOf(WideChar));
Str.Seek(0, soFromBeginning);
// read back WideString
Str.ReadBuffer(L, SizeOf(integer));
if L > 0 then begin
SetLength(W2, L);
Str.ReadBuffer(W2[1], L * SizeOf(WideChar));
end else
W2 := '';
Assert(W = W2);
finally
Str.Free;
end;
end;
WideStrings contengono una 'stringa' di WideChar di, che utilizzano 2 byte. Se si desidera memorizzare i UTF-16 (che WideStrings usano internamente) stringhe in un file, e essere in grado di utilizzare questo file in altri programmi come blocco note, è necessario scrivere un byte order mark prima:. #$FEFF
Se conosci questa, la scrittura può apparire così:
Stream1.Write(WideString1[1],Length(WideString)*2); //2=SizeOf(WideChar)
lettura può apparire così:
Stream1.Read(WideChar1,2);//assert returned 2 and WideChar1=#$FEFF
SetLength(WideString1,(Stream1.Size div 2)-1);
Stream1.Read(WideString1[1],(Stream1.Size div 2)-1);
È inoltre possibile utilizzare TFastFileStream per la lettura dei dati o stringhe, ho incollato l'unità a http://pastebin.com/ m6ecdc8c2 e un esempio riportato di seguito:
program Project36;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes,
FastStream in 'FastStream.pas';
const
WideNull: WideChar = #0;
procedure WriteWideStringToStream(Stream: TFileStream; var Data: WideString);
var
len: Word;
begin
len := Length(Data);
// Write WideString length
Stream.Write(len, SizeOf(len));
if (len > 0) then
begin
// Write WideString
Stream.Write(Data[1], len * SizeOf(WideChar));
end;
// Write null termination
Stream.Write(WideNull, SizeOf(WideNull));
end;
procedure CreateTestFile;
var
Stream: TFileStream;
MyString: WideString;
begin
Stream := TFileStream.Create('test.bin', fmCreate);
try
MyString := 'Hello World!';
WriteWideStringToStream(Stream, MyString);
MyString := 'Speed is Delphi!';
WriteWideStringToStream(Stream, MyString);
finally
Stream.Free;
end;
end;
function ReadWideStringFromStream(Stream: TFastFileStream): WideString;
var
len: Word;
begin
// Read length of WideString
Stream.Read(len, SizeOf(len));
// Read WideString
Result := PWideChar(Cardinal(Stream.Memory) + Stream.Position);
// Update position and skip null termination
Stream.Position := Stream.Position + (len * SizeOf(WideChar)) + SizeOf(WideNull);
end;
procedure ReadTestFile;
var
Stream: TFastFileStream;
my_wide_string: WideString;
begin
Stream := TFastFileStream.Create('test.bin');
try
Stream.Position := 0;
// Read WideString
my_wide_string := ReadWideStringFromStream(Stream);
WriteLn(my_wide_string);
// Read another WideString
my_wide_string := ReadWideStringFromStream(Stream);
WriteLn(my_wide_string);
finally
Stream.Free;
end;
end;
begin
CreateTestFile;
ReadTestFile;
ReadLn;
end.