6 votes

(Wide)String - stockage dans TFileStream, Delphi 7. Quelle est la méthode la plus rapide ?

J'utilise Delphi7 (VCL non unicode), j'ai besoin de stocker beaucoup de WideStrings dans un TFileStream. Je ne peux pas utiliser TStringStream car les chaînes (larges) sont mélangées avec des données binaires, le format est prévu pour accélérer le chargement et l'écriture des données ... Cependant, je pense que la façon dont je charge/écris les chaînes de caractères pourrait être un goulot d'étranglement de mon code ...

Actuellement, j'écris la longueur d'une chaîne, puis je l'écris caractère par caractère ... pendant le chargement, je charge d'abord la longueur, puis je charge char par char ...

Alors, quel est le moyen le plus rapide d'enregistrer et de charger une chaîne WideString dans TFileStream ?

Merci d'avance

6voto

Rob Kennedy Points 107381

Plutôt que de lire et d'écrire un personnage à la fois, lisez et écrivez-les tous en même temps :

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 programme Windows BSTR il peut contenir un étrange nombre d'octets. Le site Length lit le nombre d'octets et le divise par deux, il est donc possible (mais peu probable) que le code ci-dessus coupe 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 La réponse de Mghie J'ai remplacé mon Read y Write appels avec ReadBuffer y WriteBuffer . Ces derniers lèvent des exceptions s'ils ne parviennent pas à lire ou à écrire le nombre d'octets demandé.

6voto

mghie Points 25960

Les chaînes de caractères larges n'ont rien de spécial. Pour les lire et les écrire le plus rapidement possible, vous devez lire et écrire le plus 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;

2voto

Stijn Sanders Points 10468

Les chaînes larges contiennent une "chaîne" de WideChar's, qui utilisent 2 octets chacun. Si vous voulez stocker les chaînes UTF-16 (que les WideStrings utilisent en interne) dans un fichier, et être capable d'utiliser ce fichier dans d'autres programmes comme le bloc-notes, vous devez écrire un fichier de type marque de l'ordre des octets d'abord : #$FEFF .

Si vous savez cela, l'écriture peut ressembler à ça :

Stream1.Write(WideString1[1],Length(WideString)*2); //2=SizeOf(WideChar)

La 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);

1voto

dan14941 Points 217

Vous pouvez aussi utiliser TFastFileStream pour lire les données ou les chaînes de caractères, j'ai collé l'unité à http://pastebin.com/m6ecdc8c2 et un échantillon 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X