从文本文件中修改或删除一行是低级的方式吗? [英] Modifying or deleting a line from a text file the low-level way?

查看:134
本文介绍了从文本文件中修改或删除一行是低级的方式吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Delphi中的文本文件,我不希望使用加载/保存字符串列表的方法。我打算保留一个开放的filestream,在那里我读和写我的数据,保留大量的数据在硬盘而不是内存。我有一个简单的概念,将新行写入文本文件并阅读它们,但是当涉及修改和删除它们时,我找不到任何好的资源。

I'm working with a Text File in Delp and I don't wish to use the method of loading/saving with a string list. I intend to maintain an open filestream where I read and write my data there, keeping massive amounts of data on the hard disk instead of in the memory. I have the simple concept of writing new lines to a text file and reading them, but when it comes to modifying and deleting them, I cannot find any good resources.

每个该文件中的行包含一个名称和等号,其余的是数据。例如, SOMEUNIQUENAME = SomeStringValue 。我打算在线程内保持文件一段时间。该线程执行传入请求以获取,设置或删除某些数据字段。我在循环中使用 WriteLn ReadLn ,评估 EOF 。以下是我如何读取数据的示例:

Each line in this file contains a name, and equals sign, and the rest is data. For example, SOMEUNIQUENAME=SomeStringValue. I intend to keep a file open for a period of time inside of a thread. This thread performs incoming requests to either get, set, or delete certain fields of data. I use WriteLn and ReadLn in a loop, evaluating EOF. Below is an example of how I read the data:

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;

...然后我触发一个事件,通知发件人结果。请求位于队列内,这是这些请求的消息泵。线程简单地重复处理队列中的下一个请求,与典型应用程序的工作方式类似。

...and then I trigger an event which informs sender of result. The requests are inside of a queue, which is sort of a message pump for these requests. The thread simply processes the next request in the queue repeatedly, similar to how typical applications work.

我有程序准备好能够写入和删除这些字段,但是不知道我该怎么做才能真正对文件执行操作。

I have procedures ready to be able to write and delete these fields, but I don't know what I have to do to actually perform the action on the file.

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;

最后,我需要避免将整个文件加载到内存中才能完成此操作

In the end, I need to avoid loading the entire file into the memory to be able to accomplish this.

推荐答案

我发现这是一个有趣的问题,所以我做了一个小型的控制台应用程序。

I find this an interesting question, so I made a small console app.

我使用3种方法:


  • TStringList

  • Streamreader / StreamWriter

  • 文本文件

所有方法都是定时重复100次,文本文件大小为10kb和大小为1Mb的文本文件。
这是程序:

All methods are timed and repeated 100 times with a text file of 10kb in size and a text file 1Mb in size. Here is the program:

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.

输出:

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

正如您所看到的,TStringList是赢家。
由于您无法使用TStringList,所以TextFile不是一个坏的选择...

As you can see is TStringList the winner here. Since you are not able to use TStringList, TextFile is not a bad choice after all...

P.S。 :此代码省略了您必须删除输入文件的部分,并将输出文件重命名为原始文件名

P.S. : this code omits the part where you have to delete the inputfile and rename the outputfile to the original filename

这篇关于从文本文件中修改或删除一行是低级的方式吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆