(Large) String - stockage dans TFileStream, Delphi 7. Quel est le moyen le plus rapide?
-
20-09-2019 - |
Question
J'utilise Delphi7 (VCL non-unicode), je dois stocker beaucoup de WideStrings l'intérieur d'un TFileStream. Je ne peux pas utiliser TStringStream comme les cordes (large) sont mélangées avec des données binaires, le format devrait accélérer le chargement et l'écriture des données ... Cependant, je crois que je voie en cours de chargement / écriture des cordes pourrait être un goulot d'étranglement de mon code ...
actuellement j'écris longueur d'une chaîne, puis écrire carboniser par char ... lors du chargement, tout d'abord je charge la longueur, puis le chargement de charbon par char ...
Alors, quel est le meilleur moyen de sauvegarder et charger WideString à TFileStream?
Merci d'avance
La solution
Au lieu de lire et d'écrire un caractère à la fois, lire et écrire à la fois:
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;
Maintenant, techniquement, puisque WideString
est un BSTR
Windows, il peut contenir un impair nombre d'octets. La fonction Length
lit le nombre d'octets et divise par deux, il est donc possible (bien que peu probable) que le code ci-dessus coupera le dernier octet. Vous pouvez utiliser ce code à la place:
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;
Inspiré par Mghie de répondre , ont remplacé mes appels Read
et Write
avec ReadBuffer
et WriteBuffer
. Ce dernier soulèvera des exceptions si elles sont incapables de lire ou d'écrire le nombre requis d'octets.
Autres conseils
Il n'y a rien de spécial sur les chaînes larges, à lire et écrire aussi vite que possible, vous devez lire et écrire autant que possible en une seule fois:
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 contiennent une « chaîne » de WideChar de, qui utilisent 2 octets chacune. Si vous voulez stocker les chaînes UTF-16 (qui WideStrings utilisent en interne) dans un fichier, et être en mesure d'utiliser ce fichier dans d'autres programmes comme le bloc-notes, vous devez écrire un octet marque premier ordre:. #$FEFF
Si vous le savez, l'écriture peut ressembler à ceci:
Stream1.Write(WideString1[1],Length(WideString)*2); //2=SizeOf(WideChar)
lecture peut ressembler à ceci:
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);
Vous pouvez également utiliser TFastFileStream pour la lecture des données ou des chaînes, je collais l'unité http://pastebin.com/ m6ecdc8c2 et un exemple ci-dessous:
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.