2011-01-30 20 views
6

Programımda okuduğum .ed ekiyle metin dosyalarını bulabilen bir "Dosyaları Bul" işlevim var. Delphi'de Çok Sayıda Çok Dosyaya İlişkin Frekans Satırlarını Nasıl Verebilirim

enter image description here

Ben standart FindFirst/FindNext yöntemlerini kullanın ve bu çok hızlı çalışır: Şöyle explorer-benzeri bir pencerede bulunan sonuçlarını görüntülemek. Yukarıda gösterilen 584 dosya bir çift saniye içinde bulunur ve görüntülenir.

Şimdi yapmaktan hoşlandığım şey, ekrana, bu dosyaların her birinde bulunan "Kaynak" ve "Sürüm" ü gösteren iki sütun eklemek. Bu bilgiler gibi bakmak hatlarında, her dosyanın ilk 10 çizgilerin içinde genellikle bulunur:

1 SOUR FTM 
2 VERS Family Tree Maker (20.0.0.368) 

Şimdi çok hızlı bir şekilde kendimi bu ayrıştırma sorunum yok, ve ben soruyorum ne değildir.

Yardıma ihtiyacım var, bunları ilk 10 veya daha fazla satırı bu dosyalardan en hızlı şekilde nasıl yükleyeceğim, böylece bunları ayrıştırabiliyorum.

Bir StringList.LoadFromFile yapmayı denedim, ancak 1 MB'nin üzerindeki gibi büyük dosyaları yüklemek çok zaman alıyor.

Sadece ilk 10 satıra ihtiyaç duyduğumdan, onları nasıl alacağım?

Delphi 2009 kullanıyorum ve giriş dosyam Unicode olabilir veya olmayabilir, bu nedenle herhangi bir kodlama için çalışması gerekir. çalışıyor teşekkürler Antonio,

bunu yaparken sona erdi:


Takip

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 çok verimsizdir, unutun. Kutuyu düşünün ve makul (örneğin: NumLines * AvgLineLength) bayt sayısını okuyun, LineStart ile kırpın ve sonra TStrings'e bölünmüş –

+0

Aslında Worm, düşündüğünüz kadar kötü değil. Saniyede yaklaşık 10 MB okuyabilir ve yükleyebilir. Bu dosyalarda metin aramam gerektiğinde hala başarılı bir şekilde kullanıyorum. Ama neden tüm dosyaları yüklemek ve kullanıcı sadece ilk birkaç satıra ihtiyaç duyulduğunda 40 saniye beklemek için kullanın. – lkessler

cevap

14

Kullanım TFileStream ve gerektiğinde bayt okuma yöntemi okunan sayı ile. Dosyanın başlangıcında da depolanan bitmap bilgisinin okunması örneği.

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

+4

+1 Yerel işletim sistemi API'sini çok güzel bir şekilde tamamladığından, bunun için bir TFileStream kullanırdım. –

+5

+1. Sadece ilk 4 Kbayt veriyi okuyun: Bu, ilk birkaç satırı tamamen içerecek kadar büyüktür ve diskten herhangi bir şekilde okunan minimum veri miktarıdır. Çok sayıda dosyadan okuyorsanız (ve 584 dosya tam olarak "pek çok" değilse) ve fantezi almak istiyorsanız, dosyaları önbelleğe almadan, CreateFile kullanarak ve Kolu THandleStream'e iletmek isteyebilirsiniz: OS küçük bir miktar iyileşme sağladığından, işletim sistemi, büyük olasılıkla tekrar istenmeyecek olan verileri önbelleğe almamayı bilmemektedir. –

+2

TFileStream bir readLn özelliğinden yoksundur. Ya muhtemelen yeterince iyi değilse? –

4

Sadece blok (TStringList yerleşik işlevsellik kullanarak değil) okumak için dosyayı kendiniz açın ve dosyanın ilk bloğunu okuyun ve sonra örneğin strings.SetText ile StringList için bu bloğu yükleyebilirsiniz() (blok fonksiyonlarını kullanıyorsanız) veya sadece strings.LoadFromStream() bloklarınızı akışları kullanarak yüklüyorsanız.

Kişisel olarak FileRead/FileWrite blok işlevleri ile gidip bloğu bir arabelleğe yüklerim. Ayrıca similair winapi işlevlerini de kullanabilirsiniz, ancak bu hiçbir sebepten ötürü daha fazla kod.

OS, neredeyse tüm platform/dosya sistemlerinde en az 512bayt büyüklüğünde olan bloklardaki dosyaları okur, böylece önce 512 bayt okuyabilirsiniz (ve tüm satırlarınızın 10 satırını aldığınızı umarsınız, satırlarınız genellikle kısasa doğru olur) yeterli). Bu (pratik olarak) 100 veya 200 bayt okuma kadar hızlı olacaktır.

Ardından dizelerinizin nesnelerinin yalnızca 10 satırdan az olduğunu fark ederseniz, sonraki 512 bayt bloğunu okuyun ve yeniden ayrıştırmayı deneyin. (Ya da sadece 1024, 2048 ve benzeri bloklar ile geçin, birçok sistemde 512 blok kadar hızlı olacaktır; dosya sistemi kümesi boyutları genellikle 512 bayttan daha büyüktür).

PS.Ayrıca, winapi dosya işlevlerinde (CreateFile ve benzeri) iş parçacığı veya eşzamansız işlevsellik kullanarak, bu verilerinizi çalışmanızın geri kalanı çalışırken, senkronize olmayan dosyalardan yükleyebilirsiniz. Özellikle, büyük dizinlerin okunması sırasında arayüz donmayacaktır.

Bu, bilgilerinizin yüklenmesini daha hızlı gösterir (gerçekte dosya okuma listesi doğrudan yüklenir ve sonra milisaniyeden sonra geri kalan bilgiler gelir) gerçek okuma hızını artırmaz.

Bunu yalnızca diğer yöntemleri denediyseniz yapın ve ekstra güç kaynağına ihtiyacınız olduğunu düşünüyorsanız bunu yapın.

+0

FileRead/FileWrite API işlevleri –

+0

'ReadFile()' ve 'WriteFile()' Win32 API işlevleridir. 'FileRead()' ve 'FileWrite()', çevrelerindeki SysUtils paketleyicileridir. –

0

Bazen okul öncesi pascal stili o kadar da kötü değildir. Oo olmayan dosya erişimi artık çok popüler olmasa da, ReadLn(F,xxx) hala sizinki gibi durumlarda gayet iyi çalışıyor.

kolayca açın bakın veya sanal modunda bir liste görünümü kullanın ve bu listede şeyler aramak böylece bir TDictionary içine yükler bilgi aşağıda kodu (dosya adı, kaynak ve sürüm) ne zaman ondata hatta yangınları .

Uyarı: Aşağıdaki kod unicode ile çalışmaz.

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

D2009 + oku * NOT * okuma/yazma (Ln) yöntemlerinin unicode'u desteklediğini unutmayın. –

+1

-1 Soru, dosyaların @David ile aynı nedenden dolayı –

+0

-1 Unicode kodlamalarını kullanabileceğini belirtir. Unicode desteğinin olmaması, bu cevabı geçerli değildir. –

3

Böyle bir TFileStream herhangi TStream nesneden ayrı ayrı çizgiler, okumak için bir TStreamReader kullanabilir. Daha hızlı dosya G/Ç için, TCustomMemoryStream ile Bellek Eşlemeli Görünümlerini kullanabilirsiniz.

+0

TStreamReader bir readline eşdeğer yapabilir? –

+0

Remy'nin önerisine dayanarak, cevabım olarak bir örnek yazdım. –

+0

@Warren: Evet. TStreamReader'ın herkese açık bir ReadLine() yöntemi vardır. –

2

Tamam, ilk yanıtımı sildim. Remy'nin ilk önerisini kullanarak, tekrar yerleşik şeyler ile denedim. Burada sevmediğim şey, iki nesne yaratmanız ve serbest bırakmanız gerektiğidir. Ben bu kadar tamamlamayı kendi sınıfını yapmak düşünüyorum: herkes, bu unicode dosyaları ile çalışmama konusunda sorun vardı daha önce burada ne vardı ilgileniyor

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; 

edin.

+0

Alternatif için teşekkürler, Warren. Antonio'nun önerdiği gibi TFileStream'i zaten uygulamaya koymuştum ve başka bir şey denemek zorunda olmadığım kadar iyi çalışıyor. Ancak bunu bir alternatif olarak hatırlayacağım. ReadLine nedeniyle daha iyi bir çözüm için – lkessler

+0

+1, ancak bunun daha hızlı olduğundan emin değilim * –

+0

TStreamReader, ayrı bir TStream nesne işaretçisi yerine bir dosya adı belirtmenize izin veren birkaç kurucuya sahiptir. –

İlgili konular