(Wide) String - Speicherung in TFileStream, Delphi 7. Was ist der schnellste Weg?
-
20-09-2019 - |
Frage
Ich verwende Delphi7 (non-Unicode-VCL), ich brauche zu speichern viele Wide in einem TFileStream. Ich kann nicht TStringStream wie die (breit) Strings verwenden, werden mit binären Daten gemischt, wird das Format zu beschleunigen Laden projiziert und Schreiben der Daten ... Aber ich glaube, dass der Strom, wie ich bin Laden / die Saiten schreiben könnte sein, ein Engpass von meinem Code ...
zur Zeit schreibe ich Länge einer Zeichenkette, dann ist es von char char zu schreiben ... beim Laden, ich lade die Länge zuerst, dann Laden char von char ...
Also, was ist der schnellste Weg, um Wide TFileStream zu speichern und zu laden?
Vielen Dank im Voraus
Lösung
Anstatt Lese- und Schreib ein Zeichen in einer Zeit, lesen und schreiben sie alle auf einmal:
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;
Nun, technisch gesehen, da WideString
Windows BSTR
ist, kann es eine ungerade Anzahl von Bytes enthalten. Die Length
Funktion liest die Anzahl von Bytes und teilt durch zwei, so ist es möglich (wenn auch nicht wahrscheinlich), daß der Code über das letzte Byte abgeschnitten. Sie können diesen Code verwenden, anstatt:
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;
Inspiriert von mghie der Antwort haben meine Read
und Write
Anrufe mit ReadBuffer
und WriteBuffer
ersetzt. Letztere werden raise Ausnahmen, wenn sie nicht in der Lage, die angeforderte Anzahl von Bytes zu lesen oder schreiben.
Andere Tipps
Es gibt nichts Besonderes an Wide-Strings, um sie zu lesen und schreiben, so schnell wie möglich, Sie müssen so viel zu lesen und zu schreiben wie möglich in einem Rutsch:
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;
Wide enthält einen 'string' von WideChar ist, die Verwendung je 2 Bytes. Wenn Sie die UTF-16 (die Wide intern verwenden) Zeichenfolge in einer Datei gespeichert werden sollen, und in der Lage diese Datei wie Notepad in anderen Programmen zu verwenden, benötigen Sie ein Bytereihenfolgemarkierung zuerst:. #$FEFF
Wenn Sie das wissen, schriftlich wie folgt aussehen:
Stream1.Write(WideString1[1],Length(WideString)*2); //2=SizeOf(WideChar)
Lesen wie folgt aussehen:
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);
Sie können auch TFastFileStream zum Lesen der Daten oder Zeichenfolge verwenden, klebte ich das Gerät unter http://pastebin.com/ m6ecdc8c2 und eine Probe unter:
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.