(Wide) Cadena - almacenar en TFileStream, Delphi 7. ¿Cuál es la forma más rápida?
-
20-09-2019 - |
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
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.