(Large) String - stockage dans TFileStream, Delphi 7. Quel est le moyen le plus rapide?

StackOverflow https://stackoverflow.com/questions/1354092

  •  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

Était-ce utile?

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.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top