Pregunta

Estoy usando Delphi7 (no Unicode VCL), necesito almacenar gran cantidad de WideStrings dentro de un TFileStream. No puedo usar TStringStream como las cadenas (de ancho) se mezclan con datos binarios, el formato se proyecta para acelerar la carga y la escritura de los datos ... Sin embargo, creo que la forma actual Estoy cargando / escritura de las cuerdas podría ser una cuello de botella de mi código ...

Actualmente estoy escribiendo longitud de una cadena, entonces escribirlo charla por Char ... durante la carga, primero estoy cargando la longitud, entonces la carga de carbón de carbón ...

Así que, ¿cuál es la manera más rápida de guardar y cargar WideString a TFileStream?

Gracias de antemano

¿Fue útil?

Solución

En lugar de leer y escribir un carácter a la vez, leer y escribir todos a la vez:

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;

Ahora, técnicamente, ya que es un WideString BSTR de Windows, puede contener un extraña número de bytes. La función Length lee el número de bytes y se divide en dos, por lo que es posible (aunque poco probable) que el código anterior cortará el último byte. Se podría utilizar este código en su lugar:

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 de responder , han reemplazado a mis Read y Write llamadas con ReadBuffer y WriteBuffer. Este último se provoca excepciones si no son capaces de leer o escribir el número solicitado de bytes.

Otros consejos

No hay nada especial acerca de las cadenas de ancho, para leer y escribir tan rápido como sea posible es necesario leer y escribir tanto como sea posible de una sola vez:

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 contienen una 'cadena' de WideChar de, que utilizan 2 bytes cada uno. Si desea almacenar los UTF-16 (que WideStrings utilizan internamente) cadenas en un archivo, y ser capaz de utilizar este archivo en otros programas como el bloc de notas, es necesario escribir un byte de marca de orden de primero:. #$FEFF

Si usted sabe esto, la escritura puede tener este aspecto:

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

lectura puede tener este aspecto:

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

También puede utilizar TFastFileStream para leer los datos o cadenas, Pegué la unidad en http://pastebin.com/ m6ecdc8c2 y una muestra a continuación:

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.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top