2016-08-01 81 views
0

我從xml文件讀取UTF8內容,然後需要保存並按需重新加載。我從AssignFile/Writeln/Readln轉換到由David Heffernan創建的緩衝流:Buffered files (for faster disk access)如何將Readln替換爲ReadLn的緩衝流版本?

我有簡單的新的WriteLn和ReadLn程序,WriteLn可以工作,但是我無法讓ReadLn工作。

我對ReadLn概念是過程:

  1. 讀取緩衝
  2. 行查找打破
  3. 從PrevPos獲取文本到CurrPos-1
  4. 緩衝的
  5. 保存其餘添加到第一線next讀緩衝區

新寫入程序:

{ * New WriteLn * } 
procedure TForm1.Button2Click(Sender: TObject); 
var FileOut: TWriteCachedFileStream; 
    vText: string; 
    vUTF8Text: RawByteString; 
begin 
    FileOut := TWriteCachedFileStream.Create('c:\tmp\file.txt'); 
    try 
    vText := 'Delphi'; 
    vUTF8Text := Utf8Encode(vText + sLineBreak); 
    FileOut.WriteBuffer(PAnsichar(vUTF8Text)^, Length(vUTF8Text)); 
    vText := 'VB源碼'; 
    vUTF8Text := Utf8Encode(vText + sLineBreak); 
    FileOut.WriteBuffer(PAnsichar(vUTF8Text)^, Length(vUTF8Text)); 
    vText := 'Java源碼'; 
    vUTF8Text := Utf8Encode(vText + sLineBreak); 
    FileOut.WriteBuffer(PAnsichar(vUTF8Text)^, Length(vUTF8Text)); 
    finally 
    FileOut.Free; 
    end; 
end; 

但是由於讀取失敗,無法讀取文件。發生在錯誤讀取TReadOnlyCachedFileStream的功能:

錯誤說:

"Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x004069ca: write of address 0x00000010'." 


function TReadOnlyCachedFileStream.Read(var Buffer; Count: Longint): Longint; 
begin 
    ... 
    Move(CachePtr^, BufferPtr^, NumOfBytesToCopy); { <- Error occurs here } 
    ... 
end; 

這裏是我的ReadLn程序 - 不工作,因爲我無法通過的錯誤提示:

{ * New ReadLn * } 
procedure TForm1.Button3Click(Sender: TObject); 
var FileIn: TReadOnlyCachedFileStream; 
    vLinesCounter, vCurrPos, vPrevPos: integer; 
    vBuffer: TBytes; 
    vUTF8Text, vPrevUTF8Text: string; 
    vFilesize,vBytesRead,vNumberOfBytes: Int64; 
    vCh: Char; 
begin 
    vLinesCounter := 0; 
    FileIn := TReadOnlyCachedFileStream.Create('c:\tmp\file.txt'); 
    try 
    vFilesize := FileIn.Size; 
    while FileIn.Position < vFilesize do 
    begin 
     vBytesRead:=FileIn.Read(vBuffer, 65536); 
     vNumberOfBytes := vNumberOfBytes + vBytesRead; 
     {1. Find Line break 
     2. Get Text from PrevPos to CurrPos-1 
     3. Save rest of buffer to add to first line of next Read buffer} 
     vCurrPos := 0; vPrevPos := 0; 
     while vCurrPos < vBytesRead do 
     begin 
     vCh:=Chr(vBuffer[vCurrPos]); 
     if (vCh = #13) Or (vCh = #10) then { is New line } 
     begin 
      if vPrevUTF8Text <> '' then 
      vUTF8Text := vPrevUTF8Text + TEncoding.UTF8.GetString(vBuffer, vPrevPos, vCurrPos - 1) { Add previous text that was not separet line} 
      else 
      vUTF8Text := TEncoding.UTF8.GetString(vBuffer, vPrevPos, vCurrPos - 1); 
      vPrevPos := vCurrPos; { Save Pos for next line } 
      Inc(vLinesCounter); 
      Memo1.Lines.Add(vUTF8Text); 
     end; 
     end; 
     { save rest of text as start of next line } 
     if vCurrPos < Length(vBuffer) then 
     vPrevUTF8Text := TEncoding.UTF8.GetString(vBuffer, vPrevPos, vCurrPos - 1); 
    end; 
    finally 
    FileIn.Free 
    end; 
    Memo1.Lines.Add('Lines read: '+IntToStr(vLinesCounter)); 
end; 
+1

這個錯誤是因爲您的來電閱讀是錯誤的。你沒有分配一個緩衝區,即使你正在讀入指向緩衝區的指針,而不是緩衝區本身。在調用Read時,用SetLength(憑證,65536)分配緩衝區並傳遞指針(vBuffer)^。 –

+0

除此之外,Memo1.Lines.LoadFromFile真的很慢嗎? –

+0

Memo1這裏僅僅用於驗證正在讀取的內容,而不是用作主文件加載方法。好吧,現在它可以用SetLength和指針(vBuffer)^'。 –

回答

4

RTL在System.Classes單元中有自己的TStreamReaderTStreamWriter類,您應該讓他們爲您做好工作,例如:

procedure TForm1.Button2Click(Sender: TObject); 
var 
    FileOut: TStreamWriter; 
begin 
    FileOut := TStreamWriter.Create('c:\tmp\file.txt', False, TEncoding.UTF8); 
    try 
    FileOut.WriteLine('Delphi'); 
    FileOut.WriteLine('VB源碼'); 
    FileOut.WriteLine('Java源碼'); 
    finally 
    FileOut.Free; 
    end; 
end; 

procedure TForm1.Button3Click(Sender: TObject); 
var 
    FileIn: TStreamReader; 
    vLinesCounter: Integer; 
begin 
    vLinesCounter := 0; 
    FileIn := TStreamReader.Create('c:\tmp\file.txt', True); 
    try 
    while not FileIn.EndOfStream do 
    begin 
     Memo1.Lines.Add(FileIn.ReadLine); 
     Inc(vLinesCounter); 
    end; 
    finally 
    FileIn.Free; 
    end; 
    Memo1.Lines.Add('Lines read: '+IntToStr(vLinesCounter)); 
end; 

如果你想使用大衛的緩衝區類(注意,德爾福10.1柏林增添了新TBufferedFileStream類),你仍然可以做到這一點爲好,如:

procedure TForm1.Button2Click(Sender: TObject); 
var 
    FileStrm: TWriteCachedFileStream; 
    FileOut: TStreamWriter; 
begin 
    FileStrm := TWriteCachedFileStream.Create('c:\tmp\file.txt'); 
    try 
    FileOut := TStreamWriter.Create(FileStrm, TEncoding.UTF8); 
    try 
     FileOut.WriteLine('Delphi'); 
     FileOut.WriteLine('VB源碼'); 
     FileOut.WriteLine('Java源碼'); 
    finally 
     FileOut.Free; 
    end; 
    finally 
    FileStrm.Free; 
    end; 
end; 

procedure TForm1.Button3Click(Sender: TObject); 
var 
    FileStrm: TReadOnlyCachedFileStream; 
    FileIn: TStreamReader; 
    vLinesCounter: Integer; 
begin 
    vLinesCounter := 0; 
    FileStrm := TReadOnlyCachedFileStream.Create('c:\tmp\file.txt'); 
    try 
    FileIn := TStreamReader.Create(FileStrm, True); 
    try 
     while not FileIn.EndOfStream do 
     begin 
     Memo1.Lines.Add(FileIn.ReadLine); 
     Inc(vLinesCounter); 
     end; 
    finally 
     FileIn.Free; 
    end; 
    finally 
    FileStrm.Free; 
    end; 
    Memo1.Lines.Add('Lines read: '+IntToStr(vLinesCounter)); 
end; 
+0

我的文件最多可達500 MB,因此速度很重要,我認爲David的Buffered流確實比TStreamWriter快,速度更快。 –

+3

@MikeTorrettinni不要對性能進行假設 - 測試! –

+1

'TStreamReader'和'TStreamWriter'有一個內部緩衝區,所以即使底層的'TStream'沒有使用緩衝I/O,緩衝仍然在使用中。而構造函數允許你指定緩衝區大小,默認值是1KB。 –