2011-01-30 96 views
6

我的程序中有一個「查找文件」功能,它將查找帶有我的程序讀取的.ged後綴的文本文件。在我看起來像這樣的一個類似瀏覽器窗口中顯示的結果發現:如何在Delphi中高效地讀取多個文件的第一行文件

enter image description here

我用的是標準的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,所以這需要適用於任何編碼。


跟帖:感謝安東尼奧,

最後我做這個工作正常:

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

TStrings.LoadFromFile效率很低,忘了它。認真思考一下,閱讀合理的(例如:NumLines * AvgLineLength)字節數,用LineStart截斷,然後分割成TStrings – 2011-01-31 06:23:35

+0

實際上,蠕蟲並沒有你想象的那麼糟糕。它可以讀取和加載每秒大約10 MB。當我必須搜索這些文件中的文本時,我仍然可以成功使用它。但爲什麼使用它來加載整個文件,並且只需要頭幾行時就讓用戶等待40秒。 – lkessler 2011-02-01 03:36:53

回答

14

使用TFileStream的,並與所需的字節讀法讀數。這裏是讀取位於文件開頭的位圖信息的例子。

http://www.delphidabbler.com/tips/19

+4

+1我會爲此使用TFileStream,因爲它很好地包裝了本地OS文件API。 – 2011-01-30 20:57:34

+5

+1。簡單地讀取前4 KB數據:這可能足以完全包含前幾行,並且它是以任何方式從磁盤讀取的最小數據量。如果你正在從許多文件中讀取(而且584個文件並不完全是「很多」),並且你想變得很花哨,那麼你可能需要使用CreateFile來打開這些文件而不進行緩存,並將Handle傳遞給THandleStream:它可能會提供只是少量的改進,因爲操作系統知道不會緩存很可能不會再次請求的數據。 – 2011-01-31 08:14:19

+2

TFileStream缺少readLn功能。如果可能不夠好? – 2011-02-01 00:57:01

4

只要打開自己的文件塊讀取(未使用的TStringList內建功能),並讀取該文件的第一個塊,然後你就可以例如加載模塊與strings.SetText一個StringList的()(如果您使用的是塊函數),或者只是使用strings.LoadFromStream()(如果您使用流加載塊)。

我個人只是用FileRead/FileWrite塊功能,並加載塊到緩衝區。你也可以使用similair winapi函數,但這只是更多的代碼,沒有理由。

操作系統以塊爲單位讀取文件,在幾乎任何平臺/文件系統中,塊的大小至少爲512字節,因此您可以先讀取512個字節(並希望獲得全部10行,如果行數通常較短足夠)。這將會(幾乎)與讀取100或200字節一樣快。

然後如果你注意到你的字符串對象只有不到10行,那麼只要讀下一個512字節塊並嘗試再次解析即可。 (或者僅適用於1024,2048等塊,在很多系統上它可能會高達512塊,因爲文件系統簇大小通常大於512字節)。

PS。另外,在winapi文件函數(CreateFile等)中使用線程或異步功能時,您可以異步地從文件加載數據,而其他應用程序可以工作。具體來說,在讀取大型目錄期間,接口不會凍結。

這會使加載的信息顯得更快(因爲文件列表將直接加載,然後幾毫秒後其餘的信息就會出現),而實際上並沒有增加實際的閱讀速度。

只有當您嘗試了其他方法並且您覺得您需要額外的提升時才能這樣做。

0

有時老式的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. 
3

可以使用TStreamReader來讀取任何TStream對象單獨線路,如TFileStream。爲了更快的文件I/O,您可以使用內存映射視圖與TCustomMemoryStream

2

好吧,我刪除了我的第一個答案。使用雷米的第一個建議,我再次嘗試使用內置的東西。我在這裏不喜歡的是你必須創建並釋放兩個對象。我想我會做我自己的類來包裝這件事:

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文件工作的問題。

相關問題