如何用缓冲流版本的 ReadLn 替换 Readln?
How to replace Readln with Buffered stream version of ReadLn?
我从 xml 个文件中读取 UTF8 内容,然后需要保存并按需重新加载。我正在从 AssignFile/Writeln/Readln 转换为 David Heffernan 的缓冲流:Buffered files (for faster disk access)
我有简单的新 WriteLn 和 ReadLn 程序,WriteLn 可以工作,但我不能让 ReadLn 工作。
我对 ReadLn 的概念是处理:
- 读取缓冲区
- 寻找换行符
- 从 PrevPos 获取文本到 CurrPos-1
- 保存缓冲区的其余部分以添加到下一个读取缓冲区的第一行
新的 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;
但是读取有问题,因为它无法从文件读取缓冲。 TReadOnlyCachedFileStream 的 Read 函数发生错误:
错误说:
"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;
RTL 在 System.Classes
单元中有自己的 TStreamReader
and TStreamWriter
classes,你应该让他们为你做繁重的工作,例如:
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 的缓冲区 classes(注意 Delphi10.1 Berlin 增加了一个新的 TBufferedFileStream
class),你仍然可以这样做,例如:
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;
我从 xml 个文件中读取 UTF8 内容,然后需要保存并按需重新加载。我正在从 AssignFile/Writeln/Readln 转换为 David Heffernan 的缓冲流:Buffered files (for faster disk access)
我有简单的新 WriteLn 和 ReadLn 程序,WriteLn 可以工作,但我不能让 ReadLn 工作。
我对 ReadLn 的概念是处理:
- 读取缓冲区
- 寻找换行符
- 从 PrevPos 获取文本到 CurrPos-1
- 保存缓冲区的其余部分以添加到下一个读取缓冲区的第一行
新的 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;
但是读取有问题,因为它无法从文件读取缓冲。 TReadOnlyCachedFileStream 的 Read 函数发生错误:
错误说:
"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;
RTL 在 System.Classes
单元中有自己的 TStreamReader
and TStreamWriter
classes,你应该让他们为你做繁重的工作,例如:
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 的缓冲区 classes(注意 Delphi10.1 Berlin 增加了一个新的 TBufferedFileStream
class),你仍然可以这样做,例如:
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;