Reputation: 1824
I read UTF8 content from xml files and then need to save and re-load on demand. I'm converting from AssignFile/Writeln/Readln to Buffered streams by David Heffernan: Buffered files (for faster disk access)
I have simple new WriteLn and ReadLn procedures, WriteLn works, but I can't make ReadLn work.
My concept for ReadLn is to process:
New WriteLn procedure:
{ * 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;
But have problems with Read because it fails to Read to buffer from file. Error occurs in Read function of TReadOnlyCachedFileStream:
Error says:
"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;
And here is my ReadLn procedure - not working as I can't get through the error:
{ * 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;
Upvotes: 1
Views: 1297
Reputation: 596307
The RTL has its own TStreamReader
and TStreamWriter
classes in the System.Classes
unit, you should let them do the hard work for you, eg:
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;
If you want to use David's buffer classes (note that Delphi 10.1 Berlin adds a new TBufferedFileStream
class), you can still do that as well, eg:
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;
Upvotes: 6