2009-08-30 8 views
6

Ich benutze Delphi7 (Nicht-Unicode-VCL), ich muss viele WideStrings in einem TFileStream speichern. Ich kann TStringStream nicht verwenden, da die (breiten) Strings mit binären Daten gemischt werden, das Format wird so projiziert, dass das Laden und Schreiben der Daten beschleunigt wird ... Allerdings glaube ich, dass die aktuelle Art, wie ich die Strings lade/schreibe, ein sein könnte Engpass meines Codes ...(Wide) String - Speichern in TFileStream, Delphi 7. Was ist der schnellste Weg?

zur Zeit schreibe ich Länge einer Zeichenfolge, dann schreibe ich Char von Char ... während des Ladens, zuerst lade ich die Länge, dann Char von Char ...

Also, was ist der schnellste Weg zum Speichern und Laden von WideString zu TFileStream?

Vielen Dank im Voraus

+0

Ändern eines bestimmten Bereichs Ihres Codes, weil Sie * beli Eve * es könnte der Flaschenhals sein kann eine große Zeitverschwendung sein. Sie sollten zuerst messen, es gibt eine Menge von Werkzeugen, um Ihnen dort zu helfen, einige frei, einige kommerziell. Versuchen Sie diese zuerst für einige Verbindungen: http://stackoverflow.com/questions/291631/profiler-and-memory-analysis-tools-for-delphi und http://stackoverflow.com/questions/368938/delphi-profiling-tools – mghie

+0

Danke, aber ich habe QueryPerformanceCounter verwendet, um das zu erkennen;) das war sowieso der Engpass, da das Lesen von char by char sehr langsam ist ... alle anderen Operationen haben nur einige kurze Binärdaten gespeichert. – migajek

+0

Ah, OK.Ich reagierte gerade auf deine Verwendung der Wörter "glauben" und "Macht", sorry dann für das Predigen ;-) – mghie

Antwort

6

Anstatt ein Zeichen in einer Zeit lesen und schreiben, 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 ist ein Windows BSTR, kann es enthält eine ungerade Anzahl der Bytes. Die Length Funktion liest die Anzahl der Bytes und teilt sich durch zwei, so ist es möglich (obwohl nicht wahrscheinlich), dass der obige Code das letzte Byte abschneidet. 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's answer haben meine Read und Write Anrufe mit ReadBuffer und WriteBuffer ersetzt. Letzteres löst Ausnahmen aus, wenn sie die angeforderte Anzahl von Bytes nicht lesen oder schreiben können.

+0

Deine Sekunde 'WriteWideString()' Version kompiliert nicht (fehlender Typcast zu PWideChar, fehlender paren), aber wichtiger ist, dass er für leere Strings fehlschlägt. Ihre zweite 'ReadWideString()' sollte auch nach Länge 0 suchen und in diesem Fall einfach eine leere Zeichenfolge zurückgeben. – mghie

+0

Ich sehe keinen Grund, dass es nicht für leere Saiten funktionieren würde; 'SysStringByteLen' gibt Null für Nullzeiger zurück. Die Anforderung, nach "PWideChar" zu schreiben, besteht darin, dass entweder "SysStringByteLen" fälschlicherweise als "PWideChar" anstelle von "WideString" deklariert wird, oder "BSTR" fälschlicherweise als "PWideChar" anstelle von "WideString" deklariert wird. Nichtsdestotrotz habe ich das behoben und auch Ihre anderen Bedenken angesprochen. Vielen Dank. –

+0

Ich habe * einen Grund gesehen, warum es bei Strings mit nur einem Byte scheitern könnte. Wenn die Bereichsüberprüfung aktiviert ist, sollte der Ausdruck "ws [1]" in diesem Fall fehlschlagen. (Delphi QC Bug 9425 und Free Pascal Fehler 0010013 beeinflussen, ob es in einer bestimmten Version fehlschlägt.) –

6

gibt nichts über breite Saiten speziell ist, sie zu lesen und zu schreiben, so schnell wie möglich, Sie in einem Rutsch so viel wie möglich lesen und schreiben müssen:

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

Wide einen ‚string‘ von WideChar ist enthalten , die jeweils 2 Bytes verwenden. Wenn Sie die UTF-16 (die WideStrings intern verwenden) Zeichenfolgen in einer Datei speichern und diese Datei in anderen Programmen wie Notepad verwenden können, müssen Sie zuerst eine byte order mark schreiben: #$FEFF.

Wenn Sie das wissen, können schriftlich wie folgt aussehen:

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

Lesung 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); 
+1

Er sagte, dass er viele Zeichenketten speichern möchte, sie werden mit binären Daten gemischt, und ihnen werden ihre Längen vorangestellt. Definitiv nicht etwas, das mit Notepad verwandt werden soll Ihr Code reserviert den gesamten Stream auf einer einzelnen Zeichenfolge –

+0

Code, der bedingungslos auf das erste Element einer leeren Zeichenfolge zugreift, verursacht Zugriffsverletzungen – mghie

1

Sie können auch TFastFileStream zum Lesen der Daten oder Zeichenfolgen verwenden, ich klebte Einheit bei http://pastebin.com/m6ecdc8c2 und ein Beispiel unten:

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

Hinweis: dieser Code wird nicht funktionieren, wenn die zu lesende Zeichenfolge enthält alle Nullzeichen –

+0

Code, der bedingungslos auf das erste Element einer leeren Zeichenfolge zugreift, verursacht Zugriffsverletzungen – mghie

+0

Danke mghie, Code ist behoben. – pani

Verwandte Themen