2009-08-30 42 views
6

我正在使用Delphi7(非unicode VCL),我需要在TFileStream中存儲很多WideStrings。我不能使用TStringStream,因爲(寬)字符串與二進制數據混合在一起,預計格式會加速加載和寫入數據......但我相信目前我加載/寫入字符串的方式可能是我的代碼的瓶頸...(寬)字符串 - 存儲在TFileStream,Delphi 7.什麼是最快的方式?

當前我正在寫一個字符串的長度,然後寫入字符char ... 加載時,首先我加載長度,然後通過字符加載char ...

那麼,什麼是最快的方式來保存和加載WideString到TFileStream?

在此先感謝

+0

更改您的代碼的特定區域,因爲您* beli前夕*這可能是瓶頸可能是一個巨大的浪費時間。你應該先測量一下,有很多工具可以幫助你,有些是免費的,有些是商業的。首先嚐試以下鏈接:http://stackoverflow.com/questions/291631/profiler-and-memory-analysis-tools-for-delphi和http://stackoverflow.com/questions/368938/delphi-profiling-tools – mghie 2009-08-30 15:45:22

+0

謝謝,但我使用QueryPerformanceCounter來檢測;)無論如何,這是確定的瓶頸,由char讀char非常慢......所有其他操作都只是保存一些簡短的二進制數據。 – migajek 2009-08-30 17:48:31

+0

啊,好的。我只是對你使用「相信」和「可能」這兩個詞做出了反應,對於這個講道感到抱歉;-) – mghie 2009-08-31 13:59:34

回答

6

而不是讀取和寫一次一個字符,同時讀取和寫入他們都:

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; 

現在,在技術上,因爲WideString是Windows BSTR,它可以包含一個奇數字節數。 Length函數讀取字節數併除以2,所以有可能(儘管不太可能)上面的代碼將切斷最後一個字節。你可以使用此代碼來代替:

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's answer啓發,已經取代了我的ReadWrite電話與ReadBufferWriteBuffer。如果後者無法讀取或寫入所請求的字節數,則會引發異常。

+0

你的第二個'WriteWideString()'版本不會編譯(缺少向PWideChar的類型轉換,缺少paren),但更重要的是它對於空字符串失敗。你的第二個'ReadWideString()'也應該檢查長度爲0,並在這種情況下簡單地返回一個空字符串。 – mghie 2009-08-31 13:54:09

+0

我看不出沒有理由它不適用於空字符串; 'SysStringByteLen'爲空指針返回零。輸入到'PWideChar'的要求是因爲'SysStringByteLen'被錯誤地聲明爲'PWideChar'而不是'WideString',或者'BSTR'被錯誤地聲明爲'PWideChar'而不是'WideString'。儘管如此,我已經解決了這個問題並解決了您的其他問題。謝謝。 – 2009-08-31 16:36:19

+0

我*確實*看到它可能會因只有一個字節的字符串而失敗的原因。啓用範圍檢查後,表達式'ws [1]'在這種情況下應該失敗。 (Delphi QC bug 9425和Free Pascal bug 0010013會影響它是否在任何特定版本上失敗。) – 2009-08-31 16:43:55

6

沒有什麼特別之處寬字符串,閱讀和他們儘可能快地寫你需要一氣呵成讀寫儘可能:

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包含WideChar的的「字符串」 ,每個使用2個字節。如果您想要將UTF-16(WideStrings內部使用的)字符串存儲在文件中,並且能夠在其他程序(如記事本)中使用此文件,則需要首先編寫byte order mark#$FEFF

如果你知道這一點,寫作可以是這樣的:

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

閱讀可以是這樣的:

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

他說他想存儲大量的字符串,它們將與二進制數據混合在一起,並且它們將以它們的長度爲前綴。 – 2009-08-30 16:38:53

+0

無條件訪問空字符串的第一個元素的代碼將導致訪問衝突 – mghie 2009-08-31 13:58:17

1

您還可以使用TFastFileStream讀取數據或字符串,我粘貼單位http://pastebin.com/m6ecdc8c2和下面的示例:

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

注意:如果要讀取的字符串包含該代碼,則該代碼將不起作用任何空字符 – 2009-08-30 19:30:49

+0

無條件地訪問空字符串的第一個元素的代碼將導致訪問違規 – mghie 2009-08-31 13:57:40

+0

感謝mghie,代碼是固定的。 – pani 2009-08-31 15:23:27

相關問題