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