我正在使用Delphi中的文本文件,我不希望使用加载/保存字符串列表的方法.我打算维护一个打开的文件流,在那里我读取和写入我的数据,将大量数据保存在硬盘而不是内存中.我有一个简单的概念,即将新行写入文本文件并读取它们,但是当涉及到修改和删除它们时,我找不到任何好的资源.
此文件中的每一行都包含一个名称和等号,其余的是数据.例如,SOMEUNIQUENAME=SomeStringValue
.我打算在一个线程内保持文件打开一段时间.此线程执行传入请求以获取,设置或删除某些数据字段.我在循环中使用WriteLn
并ReadLn
进行评估EOF
.以下是我如何读取数据的示例:
FFile = TextFile;
...
function TFileWrapper.ReadData(const Name: String): String;
var
S: String; //Temporary line to be parsed
N: String; //Temporary name of field
begin
Result:= '';
Reset(FFile);
while not EOF(FFile) do begin
ReadLn(FFile, S);
N:= UpperCase(Copy(S, 1, Pos('=', S)-1));
if N = UpperCase(Name) then begin
Delete(S, 1, Pos('=', S));
Result:= S;
Break;
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
...然后我触发一个事件,通知发送者结果.请求在队列中,这是这些请求的消息泵.线程只是重复处理队列中的下一个请求,类似于典型应用程序的工作方式.
我已准备好编写和删除这些字段的程序,但我不知道我必须做什么来实际对文件执行操作.
procedure TFileWrapper.WriteData(const Name, Value: String);
var
S: String; //Temporary line to be parsed
N: String; //Temporary name of field
begin
Result:= '';
Reset(FFile);
while not EOF(FFile) do begin
ReadLn(FFile, S);
N:= UpperCase(Copy(S, 1, Pos('=', S)-1));
if N = UpperCase(Name) then begin
//How to re-write this line?
Break;
end;
end;
end;
procedure TFileWrapper.DeleteData(const Name: String);
var
S: String; //Temporary line to be parsed
N: String; //Temporary name of field
begin
Result:= '';
Reset(FFile);
while not EOF(FFile) do begin
ReadLn(FFile, S);
N:= UpperCase(Copy(S, 1, Pos('=', S)-1));
if N = UpperCase(Name) then begin
//How to delete this line?
Break;
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
最后,我需要避免将整个文件加载到内存中才能完成此操作.
我发现这是一个有趣的问题,所以我做了一个小型控制台应用程序.
我用了3种方法:
所有方法都定时并重复100次,文本文件大小为10kb,文本文件大小为1Mb.这是程序:
program Project16;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes, StrUtils, Diagnostics, IOUtils;
procedure DeleteLine(StrList: TStringList; SearchPattern: String);
var
Index : Integer;
begin
for Index := 0 to StrList.Count-1 do
begin
if ContainsText(StrList[Index], SearchPattern) then
begin
StrList.Delete(Index);
Break;
end;
end;
end;
procedure DeleteLineWithStringList(Filename : string; SearchPattern : String);
var StrList : TStringList;
begin
StrList := TStringList.Create;
try
StrList.LoadFromFile(Filename);
DeleteLine(StrList, SearchPattern);
// don't overwrite our input file so we can test
StrList.SaveToFile(TPath.ChangeExtension(Filename, '.new'));
finally
StrList.Free;
end;
end;
procedure DeleteLineWithStreamReaderAndWriter(Filename : string; SearchPattern : String);
var
Reader : TStreamReader;
Writer : TStreamWriter;
Line : String;
DoSearch : Boolean;
DoWrite : Boolean;
begin
Reader := TStreamReader.Create(Filename);
Writer := TStreamWriter.Create(TPath.ChangeExtension(Filename, '.new'));
try
DoSearch := True;
DoWrite := True;
while Reader.Peek >= 0 do
begin
Line := Reader.ReadLine;
if DoSearch then
begin
DoSearch := not ContainsText(Line, SearchPattern);
DoWrite := DoSearch;
end;
if DoWrite then
Writer.WriteLine(Line)
else
DoWrite := True;
end;
finally
Reader.Free;
Writer.Free;
end;
end;
procedure DeleteLineWithTextFile(Filename : string; SearchPattern : String);
var
InFile : TextFile;
OutFile : TextFile;
Line : String;
DoSearch : Boolean;
DoWrite : Boolean;
begin
AssignFile(InFile, Filename);
AssignFile(OutFile, TPath.ChangeExtension(Filename, '.new'));
Reset(InFile);
Rewrite(OutFile);
try
DoSearch := True;
DoWrite := True;
while not EOF(InFile) do
begin
Readln(InFile, Line);
if DoSearch then
begin
DoSearch := not ContainsText(Line, SearchPattern);
DoWrite := DoSearch;
end;
if DoWrite then
Writeln(OutFile, Line)
else
DoWrite := True;
end;
finally
CloseFile(InFile);
CloseFile(OutFile);
end;
end;
procedure TimeDeleteLineWithStreamReaderAndWriter(Iterations : Integer);
var
Count : Integer;
Sw : TStopWatch;
begin
Writeln(Format('Delete line with stream reader/writer - file 10kb, %d iterations', [Iterations]));
Sw := TStopwatch.StartNew;
for Count := 1 to Iterations do
DeleteLineWithStreamReaderAndWriter('c:\temp\text10kb.txt', 'thislinewillbedeleted=');
Sw.Stop;
Writeln(Format('Elapsed time : %d milliseconds', [Sw.ElapsedMilliseconds]));
Writeln(Format('Delete line with stream reader/writer - file 1Mb, %d iterations', [Iterations]));
Sw := TStopwatch.StartNew;
for Count := 1 to Iterations do
DeleteLineWithStreamReaderAndWriter('c:\temp\text1Mb.txt', 'thislinewillbedeleted=');
Sw.Stop;
Writeln(Format('Elapsed time : %d milliseconds', [Sw.ElapsedMilliseconds]));
end;
procedure TimeDeleteLineWithStringList(Iterations : Integer);
var
Count : Integer;
Sw : TStopWatch;
begin
Writeln(Format('Delete line with TStringlist - file 10kb, %d iterations', [Iterations]));
Sw := TStopwatch.StartNew;
for Count := 1 to Iterations do
DeleteLineWithStringList('c:\temp\text10kb.txt', 'thislinewillbedeleted=');
Sw.Stop;
Writeln(Format('Elapsed time : %d milliseconds', [Sw.ElapsedMilliseconds]));
Writeln(Format('Delete line with TStringlist - file 1Mb, %d iterations', [Iterations]));
Sw := TStopwatch.StartNew;
for Count := 1 to Iterations do
DeleteLineWithStringList('c:\temp\text1Mb.txt', 'thislinewillbedeleted=');
Sw.Stop;
Writeln(Format('Elapsed time : %d milliseconds', [Sw.ElapsedMilliseconds]));
end;
procedure TimeDeleteLineWithTextFile(Iterations : Integer);
var
Count : Integer;
Sw : TStopWatch;
begin
Writeln(Format('Delete line with text file - file 10kb, %d iterations', [Iterations]));
Sw := TStopwatch.StartNew;
for Count := 1 to Iterations do
DeleteLineWithTextFile('c:\temp\text10kb.txt', 'thislinewillbedeleted=');
Sw.Stop;
Writeln(Format('Elapsed time : %d milliseconds', [Sw.ElapsedMilliseconds]));
Writeln(Format('Delete line with text file - file 1Mb, %d iterations', [Iterations]));
Sw := TStopwatch.StartNew;
for Count := 1 to Iterations do
DeleteLineWithTextFile('c:\temp\text1Mb.txt', 'thislinewillbedeleted=');
Sw.Stop;
Writeln(Format('Elapsed time : %d milliseconds', [Sw.ElapsedMilliseconds]));
end;
begin
try
TimeDeleteLineWithStringList(100);
TimeDeleteLineWithStreamReaderAndWriter(100);
TimeDeleteLineWithTextFile(100);
Writeln('Press ENTER to quit');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Run Code Online (Sandbox Code Playgroud)
输出:
Delete line with TStringlist - file 10kb, 100 iterations
Elapsed time : 188 milliseconds
Delete line with TStringlist - file 1Mb, 100 iterations
Elapsed time : 5137 milliseconds
Delete line with stream reader/writer - file 10kb, 100 iterations
Elapsed time : 456 milliseconds
Delete line with stream reader/writer - file 1Mb, 100 iterations
Elapsed time : 22382 milliseconds
Delete line with text file - file 10kb, 100 iterations
Elapsed time : 250 milliseconds
Delete line with text file - file 1Mb, 100 iterations
Elapsed time : 9656 milliseconds
Press ENTER to quit
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,TStringList是赢家.既然你无法使用TStringList,那么TextFile毕竟不是一个糟糕的选择......
PS:此代码省略了必须删除输入文件的部分,并将输出文件重命名为原始文件名
如果不将整个文件加载到容器中TStringList
,您唯一的选择是:
所以回答你的具体问题:
if N = UpperCase(Name) then begin
//How to re-write this line?
Break;
end;
Run Code Online (Sandbox Code Playgroud)
将新输出写入第二个(输出)文件.
if N = UpperCase(Name) then begin
//How to delete this line?
Break;
end;
Run Code Online (Sandbox Code Playgroud)
只需跳过将WriteLn
指示的行输出到第二个(输出)文件.
当你可以简单地说:"我不想使用TStringList"的人为限制只会使你的任务复杂化:
TStringList
使用中LoadFromFile
IndexOf()
TStringList
TStringList.SaveToFile
我发现的唯一理由不使用TStringList
来执行这些类型的操作已在该文件的大小超过的能力TStringList
(从未发生过),或与文件是文本,但不是真正的"行"打交道时导向(例如,EDI文件通常是一个非常长的单行文本,或者XML文件可能不包含换行符,因此也是一个非常长的单行文本).但是,即使在EDI或XML的情况下,也经常将它们加载到a中TStringList
,转换为基于行的格式(插入换行符或其他),并从字符串列表中进行检索.
归档时间: |
|
查看次数: |
11116 次 |
最近记录: |