2009-08-30 20 views
6

Estoy usando Delphi7 (VCL no unicode), necesito almacenar muchos WideStrings dentro de un TFileStream. No puedo usar TStringStream ya que las cadenas (anchas) se mezclan con datos binarios, el formato se proyecta para acelerar la carga y escritura de los datos ... Sin embargo, creo que la forma actual en que estoy cargando/escribiendo las cadenas podría ser una cuello de botella de mi código ...(Ancho) Cadena - almacenamiento en TFileStream, Delphi 7. ¿Cuál es la manera más rápida?

Actualmente estoy escribiendo la longitud de una cuerda, y luego la escribo char por char ... durante la carga, primero estoy cargando la longitud, luego cargando por char ...

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

Gracias de antemano

+0

Cambiar un área particular de su código porque * beli Eva * puede ser el cuello de botella puede ser una gran pérdida de tiempo. Primero debe medir, hay muchas herramientas para ayudarlo, algunas gratis, algunas comerciales. Pruebe estos primero para algunos enlaces: http://stackoverflow.com/questions/291631/profiler-and-memory-analysis-tools-for-delphi y http://stackoverflow.com/questions/368938/delphi-profiling-tools – mghie

+0

Gracias, pero estaba usando QueryPerformanceCounter para detectar eso;) de todos modos ese fue el cuello de botella seguro, ya que leer char por char es muy lento ... todas las demás operaciones solo guardaban algunos datos binarios cortos. – migajek

+0

Ah, está bien.Estaba reaccionando sobre el uso de las palabras "creer" y "poder", perdón por la predicación ;-) – mghie

Respuesta

6

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 WideString es un Windows BSTR, lo que puede contiene un número impar de de bytes. La función Length lee el número de bytes y los divide por dos, por lo que es posible (aunque no probable) que el código anterior corte 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; 

Inspirado por Mghie's answer, han reemplazado a mis Read y Write llamadas con ReadBuffer y WriteBuffer. Este último levantará excepciones si no pueden leer o escribir el número solicitado de bytes.

+0

Tu segundo La versión 'WriteWideString()' no compila (falta el tipo de difusión a PWideChar, falta paren), pero lo más importante es que falla para las cadenas vacías. Su segundo 'ReadWideString()' también debería verificar la longitud 0 y simplemente devolver una cadena vacía en ese caso. – mghie

+0

No veo ninguna razón por la que no funcione para cadenas vacías; 'SysStringByteLen' devuelve cero para punteros nulos. El requisito de tipo-fundido a 'PWideChar' es porque ya sea' SysStringByteLen' se declararon bien para tomar una 'PWideChar' en lugar de' WideString', o 'BSTR' se declararon bien ser' PWideChar' en lugar de 'WideString'. No obstante, lo he solucionado y he abordado sus otras inquietudes también. Gracias. –

+0

I * did * veo una razón por la que podría fallar para las cadenas que tienen solo un byte. Con la verificación de rango habilitada, la expresión 'ws [1]' debería fallar en ese caso. (Delphi QC bug 9425 y Free Pascal bug 0010013 afectan si falla en cualquier versión en particular). –

6

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

WideStrings contienen una 'cadena' de WideChar de , que usan 2 bytes cada uno Si desea almacenar el UTF-16 (que WideStrings usa internamente) cadenas en un archivo, y poder usar este archivo en otros programas como el bloc de notas, primero debe escribir un byte order mark: #$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); 
+1

Dijo que quiere almacenar gran cantidad de cadenas, que van a ser entremezclados con datos binarios, y que van a ser prefijado por sus longitudes definitivamente no. algo para ser utilizado con el Bloc de notas Su código dedica todo el flujo a una sola cadena –

+0

código acceder incondicionalmente el primer elemento de una cadena vacía hará que violaciónes de acceso – mghie

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. 
+2

Nota:... Ese código no funcionará si la cadena se lea contiene cualquier carácter nulo –

+0

El código que acceda sin condiciones al primer elemento de una cadena vacía provocará violaciones de acceso. – mghie

+0

Gracias mghie, el código está solucionado. – pani

Cuestiones relacionadas