如何有效地读取Delphi中许多文件的前几行

如何有效地读取Delphi中许多文件的前几行,delphi,file-io,large-files,Delphi,File Io,Large Files,我的程序中有一个“查找文件”功能,可以查找程序读取的带有.ged后缀的文本文件。我在类似于资源管理器的窗口中显示找到的结果,如下所示: 我使用标准的FindFirst/FindNext方法,这非常快。上面显示的584个文件将在几秒钟内找到并显示 我现在要做的是在显示中添加两列,显示每个文件中包含的“源”和“版本”。此信息通常位于每个文件的前10行中,这些行看起来像: 1 SOUR FTM 2 VERS Family Tree Maker (20.0.0.368) 现在我自己很快就可以解析这个

我的程序中有一个“查找文件”功能,可以查找程序读取的带有.ged后缀的文本文件。我在类似于资源管理器的窗口中显示找到的结果,如下所示:

我使用标准的FindFirst/FindNext方法,这非常快。上面显示的584个文件将在几秒钟内找到并显示

我现在要做的是在显示中添加两列,显示每个文件中包含的“源”和“版本”。此信息通常位于每个文件的前10行中,这些行看起来像:

1 SOUR FTM
2 VERS Family Tree Maker (20.0.0.368)
现在我自己很快就可以解析这个,这不是我要问的

我需要的帮助只是如何最快地从这些文件中加载前10行左右的内容,以便我能够解析它们

我尝试过使用StringList.LoadFromFile,但加载大文件(例如大于1MB的文件)需要花费太多时间

既然我只需要前10行左右,我怎么才能最好地得到它们

我使用的是Delphi2009,我的输入文件可能是Unicode,也可能不是Unicode,所以这需要用于任何编码


后续:谢谢安东尼奥

最后我做了一个很好的工作:

var
  CurFileStream: TStream;
  Buffer: TBytes;
  Value: string;
  Encoding: TEncoding;

try
  CurFileStream := TFileStream.Create(folder + FileName, fmOpenRead);
  SetLength(Buffer, 256);
  CurFileStream.Read(Buffer[0], 256);
  TEncoding.GetBufferEncoding(Buffer, Encoding);
  Value := Encoding.GetString(Buffer);
  ...
  (parse through Value to get what I want)
  ...
finally
  CurFileStream.Free;
end;

使用TFileStream和Read方法读取所需的字节数。下面是读取位图信息的示例,位图信息也存储在文件开头


只需自己打开文件进行块读取(不使用TStringList内置功能),然后读取文件的第一个块,然后您可以使用strings.SetText()(如果使用块函数)或strings.LoadFromStream()将该块加载到stringlist中(如果使用流加载块)

我个人只需要使用FileRead/FileWrite块函数,并将块加载到缓冲区中。您也可以使用类似的winapi函数,但这只是无缘无故地增加代码

操作系统以块的形式读取文件,几乎在任何平台/文件系统上都至少有512字节大,因此您可以先读取512字节(希望您获得全部10行,如果您的行通常足够短,这将是正确的)。这将(实际上)与读取100或200字节一样快

然后,如果您注意到字符串对象只有不到10行,只需读取下一个512字节的块,然后再次尝试解析。(或者只使用1024、2048等块,在许多系统上,它的速度可能会达到512块,因为文件系统集群大小通常大于512字节)

另外,使用winapi文件函数(CreateFile等)中的线程或异步功能,您可以在应用程序的其余部分工作时从文件异步加载数据。具体来说,在读取大目录时,接口不会冻结

这将加快信息的加载速度(因为文件列表将直接加载,然后几毫秒后,其余信息将出现),但实际上不会提高实际的读取速度


只有当你尝试过其他方法,并且觉得需要额外的激励时,才可以这样做。

有时候老派的帕斯卡风格并没有那么糟糕。 尽管非oo文件访问似乎不再流行,
ReadLn(F,xxx)
在像您这样的情况下仍然可以正常工作

下面的代码将信息(文件名、源代码和版本)加载到
t字典
中,以便您可以轻松地查找它,或者您可以在虚拟模式下使用listview,甚至在触发
ondata
时在此列表中查找内容

警告:下面的代码不适用于unicode

program Project101;
{$APPTYPE CONSOLE}

uses
  IoUtils, Generics.Collections, SysUtils;

type
  TFileInfo=record
    FileName,
    Source,
    Version:String;
  end;

function LoadFileInfo(var aFileInfo:TFileInfo):Boolean;
var
  F:TextFile;
begin
  Result := False;
  AssignFile(F,aFileInfo.FileName);
  {$I-}
  Reset(F);
  {$I+}
  if IOResult = 0 then
  begin
    ReadLn(F,aFileInfo.Source);
    ReadLn(F,aFileInfo.Version);
    CloseFile(F);
    Exit(True)
  end
  else
    WriteLn('Could not open ', aFileInfo.FileName);
end;

var
  FileInfo:TFileInfo;
  Files:TDictionary<string,TFileInfo>;
  S:String;
begin
  Files := TDictionary<string,TFileInfo>.Create;
  try
    for S in TDirectory.GetFiles('h:\WINDOWS\system32','*.xml') do
    begin
      WriteLn(S);
      FileInfo.FileName := S;
      if LoadFileInfo(FileInfo) then
        Files.Add(S,FileInfo);
    end;

    // showing file information...
    for FileInfo in Files.Values do
      WriteLn(FileInfo.Source, ' ',FileInfo.Version);
  finally
    Files.Free
  end;
  WriteLn;
  WriteLn('Done. Press any key to quit . . .');
  ReadLn;
end.
程序项目101;
{$APPTYPE控制台}
使用
IoUtils,泛型。集合,SysUtils;
类型
TFileInfo=记录
文件名,
来源:,
版本:字符串;
结束;
函数LoadFileInfo(var aFileInfo:TFileInfo):布尔值;
变量
F:文本文件;
开始
结果:=假;
AssignFile(F,aFileInfo.FileName);
{$I-}
重置(F);
{$I+}
如果IOResult=0,则
开始
ReadLn(F,aFileInfo.Source);
ReadLn(F,aFileInfo.Version);
关闭文件(F);
退出(真)
结束
其他的
WriteLn('无法打开',aFileInfo.FileName);
结束;
变量
FileInfo:TFileInfo;
文件:t字典;
S:字符串;
开始
文件:=TDictionary.Create;
尝试
对于TDirectory.GetFiles('h:\WINDOWS\system32','*.xml')中的
开始
书面文件(S);;
FileInfo.FileName:=S;
如果LoadFileInfo(FileInfo),则
文件。添加(S,FileInfo);
结束;
//正在显示文件信息。。。
用于文件中的文件信息。值为
WriteLn(FileInfo.Source',FileInfo.Version);
最后
文件。免费
结束;
书面语;
WriteLn('完成。按任意键退出…);
ReadLn;
结束。

您可以使用
TStreamReader
从任何
TStream
对象(例如
TFileStream
)读取单独的行。为了更快地进行文件I/O,您可以将内存映射视图与
TCustomMemoryStream

一起使用。好的,我删除了我的第一个答案。使用上面雷米的第一个建议,我再次尝试使用内置的东西。我不喜欢的是,你必须创建并释放两个对象。我想我会制作自己的课程来总结这一点:

var
  fs:TFileStream;
  tr:TTextReader;
  filename:String;
begin
  filename :=  'c:\temp\textFileUtf8.txt';
  fs := TFileStream.Create(filename, fmOpenRead);
  tr := TStreamReader.Create(fs);
  try
      Memo1.Lines.Add( tr.ReadLine );

  finally
    tr.Free;
    fs.Free;
  end;   
end;

如果有人对我以前在这里的内容感兴趣,那么它就有一个问题,那就是不能使用unicode文件。

+1我会使用TFileStream,因为它很好地包装了本机OS文件API。+1。只需读取前4 KB的数据:这可能足以完全包含前几行,而且这是从磁盘读取的最小数据量。如果您正在读取许多文件(而584个文件并不完全是“很多”),并且您希望获得更多乐趣,那么您可能希望在不缓存、使用CreateFile并将句柄传递给THandleStream的情况下打开这些文件:这可能会提供一点小小的改进,因为操作系统知道不需要cac