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

È stato utile?

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.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top