我的程序中有一个“查找文件”功能,可以找到程序读取的带有.ged后缀的文本文件。我在类似于资源管理器的窗口中显示找到的结果,如下所示:
我使用标准的FindFirst / FindNext方法,这非常有效。上面显示的584个文件可在几秒钟内找到并显示。
我现在要做的是在显示屏上添加两列,显示每个文件中包含的“Source”和“Version”。此信息通常位于每个文件的前10行内,如下所示:
1 SOUR FTM
2 VERS Family Tree Maker (20.0.0.368)
现在我自己解析这个问题没有问题,这不是我要问的问题。
我需要帮助的是如何最快速地从这些文件加载前10行,以便我可以解析它们。
我试过做一个StringList.LoadFromFile,但加载大文件需要花费太多时间,例如1 MB以上的文件。
由于我只需要前10行左右,我最好如何获得它们?
我正在使用Delphi 2009,我的输入文件可能是也可能不是Unicode,所以这需要适用于任何编码。
跟进:谢谢Antonio,
我最终这样做了,效果很好:
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;
答案 0 :(得分:14)
使用TFileStream并使用Read方法读取所需的字节数。以下是读取位图信息的示例,该位图信息也存储在文件的开头。
答案 1 :(得分:4)
只需自己打开文件进行块读取(不使用TStringList内置功能),然后读取文件的第一个块,然后你就可以将该块加载到带有strings.SetText()的字符串列表中(如果你是如果使用流加载块,则使用块函数)或简单地使用strings.LoadFromStream()。
我个人只是使用FileRead / FileWrite块函数,并将块加载到缓冲区中。你也可以使用similair winapi函数,但这只是无用的代码。
操作系统读取块中的文件,这些块几乎在任何平台/文件系统上都至少有512字节,所以你可以先读取512个字节(并希望你得到所有10行,如果你的行通常足够短,这将是真的)。这(实际上)与读取100或200字节一样快。
然后,如果您注意到您的字符串对象只有少于10行,则只需读取下一个512字节块并尝试再次解析。 (或者只是使用1024,2048等块,在许多系统上它可能会快512块,因为文件系统簇大小通常大于512字节)。
PS。此外,在winapi文件函数(CreateFile等)中使用线程或异步功能,您可以异步加载文件中的数据,而其他应用程序可以正常工作。具体来说,在读取大目录期间,接口不会冻结。
这将使您的信息加载速度更快,(因为文件列表将直接加载,然后几毫秒后其余信息将会出现),而实际上并没有提高实际读取速度。
只有在尝试过其他方法并且您觉得需要额外提升时才这样做。
答案 2 :(得分:3)
您可以使用TStreamReader
来读取任何TStream
对象中的各个行,例如TFileStream
。对于更快的文件I / O,您可以使用TCustomMemoryStream
的内存映射视图。
答案 3 :(得分:2)
好的,我删除了我的第一个答案。使用上面的Remy的第一个建议,我再次尝试使用内置的东西。我不喜欢这里是你必须创建和释放两个对象。我想我会把自己的课程包起来:
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文件的问题。
答案 4 :(得分:0)
有时候oldschool pascal stylee也不是那么糟糕。
即使非oo文件访问似乎不再受欢迎,ReadLn(F,xxx)
在像你这样的情况下仍然可以正常工作。
下面的代码将信息(文件名,来源和版本)加载到TDictionary
中,以便您可以轻松查找,或者您可以在虚拟模式下使用列表视图,并在此列表中查找内容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.