如何用ReadLn的缓冲流版本替换Readln?

时间:2016-08-01 22:23:16

标签: delphi filestream delphi-xe7

我从xml文件中读取UTF8内容,然后需要按需保存和重新加载。我正在从DavidFaffernan的AssignFile / Writeln / Readln转换为缓冲流:Buffered files (for faster disk access)

我有简单的新WriteLn和ReadLn程序,WriteLn工作,但我无法使ReadLn工作。

我对ReadLn的概念是处理:

  1. 读取缓冲区
  2. 查找换行符
  3. 从PrevPos获取文本到CurrPos-1
  4. 保存剩余的缓冲区以添加到下一个读缓冲区的第一行
  5. 新的WriteLn程序:

    { * 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;
    

    但是Read有问题,因为它无法从文件中读取缓冲区。 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 个答案:

答案 0 :(得分: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;

如果你想使用David的缓冲类(注意Delphi 10.1 Berlin增加了一个新的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;