![]() |
schnell Zeilen zählen
Hallo,
Ich bin gerade dabei, einen Code zu schreiben, der schnell die Zeilen einer Textdatei zählt. Zuerst hatte ich das, funktioniert zwar sehr gut, ist aber für große Dateien einfach zu langsam.
Delphi-Quellcode:
Dann hatte ich die Idee, die Datei in Blöcken auszulesen und dann die Zeilenumbrüche zu zählen.
function CountLines1(const sFile: String): Integer;
var fInput: TextFile; begin Result := 0; AssignFile(fInput, sFile); try ReSet(fInput); try while not EOF(fInput) do begin ReadLn(fInput); Inc(Result); end; finally CloseFile(fInput); end; except //Result := -1; end; end; Diese Methode arbeitet viel schneller, nur leider gibt sie immer (meistens) zuviele Zeilen zurück. :gruebel:
Delphi-Quellcode:
Wäre nett, wenn sich das mal jmd. anschauen könnte und mir sagen kann, wieso der 2. Code nicht richtig funktioniert.
function PosCount(const SubStr, S: String): Integer;
var iPos: Integer; begin Result := 0; iPos := 0; repeat iPos := PosEx(SubStr, S, Succ(iPos)); if iPos > 0 then Inc(Result); until iPos = 0; end; function CountLines2(const sFile: String): Integer; const BUFFER_SIZE = 8192; var fInput: File; cBuffer: Array[1..BUFFER_SIZE] of Char; iRead: Integer; begin Result := 0; AssignFile(fInput, sFile); try ReSet(fInput, 1); try while not EOF(fInput) do begin BlockRead(fInput, cBuffer, BUFFER_SIZE, iRead); Inc(Result, PosCount(#10, cBuffer)); end; finally CloseFile(fInput); end; except //Result := -1; end; end; Danke! |
Re: schnell Zeilen zählen
Vielleicht solltest du mal nur den Teil in cBuffer auswerten, der auch von BlockRead mit Daten gefüllt wurde?
etwa so:
Code:
Inc(Result, PosCount(#10, [color=red]Copy(cBuffer, 1, iRead)[/color]));
PS: es wäre eventuell auch hilfreich, wenn du erwähnt hättes um wieviel falsch gezählt wird. [ot] ach ja, das ist ein Reset und kein Re-Set :stupid: |
Re: schnell Zeilen zählen
Wenns nur um simple Textdateien geht:
Delphi-Quellcode:
function GetLineCount(AFile:String):Integer;
var str : Tstrings; begin str := Tstringlist.create; str.loadfromfile(Afile); result := str.count; str.free; end; |
Re: schnell Zeilen zählen
Zitat:
Danke :spin2: @Ghostwalker Für kleine Dateien gut, aber für große ungeeignet. //Für eine 120 MB Datei, braucht die 2. Funktion ~ 350 ms, wenn das nicht mal schnell ist! :-D |
Re: schnell Zeilen zählen
Moin Ghostwalker,
das hab' ich früher auch mal so gemacht, und wenn es sich vornehmlich um kleine Textdateien handelt, ist das wohl auch noch gangbar, aber je grösser diese werden, umso schneller wirst Du merken, dass das vollständige Laden in den Arbeitsspeicher, und anschliessende Ermitteln der Zeilen ganz böse auf die Performance drückt. Allein schon das zeilenweise auslesen, geht da schon erheblich schneller. |
Re: schnell Zeilen zählen
Zitat:
|
Re: schnell Zeilen zählen
abgesehn wenn die Datei größer als der freie Speicher (RAM) ist und dieses auch speichermäßig etwas unökonomisch ist (an dem Beispiel über 120 MB welche die TStringList in den RAM lädt gegen 8 KB mit CountLines2)
und soll ich dir mal 'ne "kleine" 5 GB Textdatei leihen? @akko5: ein größerer Speicherblock könnte nicht schaden, damit verringern sich die Lesezugriffe und das ganze ist auch etwas optimaler in Bezug auf die WindowsFileCache/Dateiverwaltung.
Delphi-Quellcode:
BUFFER_SIZE = 65536; // 64 KB
|
Re: schnell Zeilen zählen
Zitat:
So sollte die Funktion perfekt sein:
Delphi-Quellcode:
Für eine ~100 MB Textdatei (9105111 Zeilen) brauchte die Funktion 296 ms. :thumb:
function PosCount(const SubStr, S: String): Integer;
var iPos: Integer; begin Result := 0; iPos := 0; repeat iPos := PosEx(SubStr, S, Succ(iPos)); if iPos > 0 then Inc(Result); until iPos = 0; end; function CountLines(const sFile: String): Integer; const BUFFER_SIZE = 65536; var fInput: File; cBuffer: Array[1..BUFFER_SIZE] of Char; iRead: Integer; sBuffer: String; begin Result := 0; AssignFile(fInput, sFile); try ReSet(fInput, 1); try while not EOF(fInput) do begin BlockRead(fInput, cBuffer, BUFFER_SIZE, iRead); sBuffer := Copy(cBuffer, 1, iRead); Inc(Result, PosCount(#10, sBuffer)); end; if sBuffer[Length(sBuffer)] <> #10 then Inc(Result); finally CloseFile(fInput); end; except //Result := -1; end; end; MfG |
Re: schnell Zeilen zählen
iRead, falls die Letzte Zeile nicht den Puffer ausfüllte ;)
Delphi-Quellcode:
direkt in den String eingelesen (PosCount/PosEx möchten das ja eh in einem String) und intern gezählt:
iRead := 0;
while not EOF(fInput) do begin BlockRead(fInput, cBuffer, BUFFER_SIZE, iRead); sBuffer := Copy(cBuffer, 1, iRead); Inc(Result, PosCount(#10, Copy(cBuffer, 1, iRead))); end; if (iRead > 0) and (sBuffer[iRead] <> #10) then Inc(Result);
Delphi-Quellcode:
function CountLines2(const sFile: String): Integer;
const BUFFER_SIZE = 65536; var fInput: File; S: String; iRead, iPos: Integer; begin Result := 0; AssignFile(fInput, sFile); try Reset(fInput, 1); try iRead := 0; while not EOF(fInput) do begin SetLength(S, BUFFER_SIZE); BlockRead(fInput, S[1], BUFFER_SIZE, iRead); SetLength(S, iRead); iPos := 0; repeat iPos := PosEx(#0, S, Succ(iPos)); if iPos > 0 then Inc(Result); until iPos = 0; end; if (iRead > 0) and (S[iRead] <> #10) then Inc(Result); finally CloseFile(fInput); end; except //Result := -1; end; end; |
Re: schnell Zeilen zählen
Moin Akko,
das ist ja mal ein interessantes Problem ;-) Probier' das hier mal aus:
Delphi-Quellcode:
Auf dem gleichen Rechner mit der gleichen Datei getestet, ist die noch einmal schneller als Deine ;-)
function csCountLines(const AsFilepath : string) : Integer;
const BUFSIZE = 65536; var fs : TFileStream; sBuf : array [1..BUFSIZE] of char; iLines : Integer; iRead : integer; i : Integer; begin fs := TFileStream.Create(AsFilepath,fmOpenRead); try iLines := 0; iRead := fs.Read(sBuf,BUFSIZE); while iRead = BUFSIZE do begin for i := 1 to iRead do begin Inc(iLines,Ord(sBuf[i]=#10)); end; iRead := fs.Read(sBuf,BUFSIZE); end; for i := 1 to iRead do begin Inc(iLines,Ord(sBuf[i]=#10)); end; if (iRead > 0) and (sBuf[iRead] <> #10) then inc(iLines); finally fs.Free; end; Result := iLines; end; Bei den offensichtlich sehr kurzen Zeilen, die Du da hast, ist das sequenzielle Durchsuchen des Buffers offensichtlich effizienter als die Sprünge mit PosEx. @Philipp: Da hast Du wohl recht. Inzwischen ist die Verwendung von TStringList wohl doch schneller als das rein zeilenweise Lesen. Zuletzt hatte ich das unter NT 4 mit einem in D2 geschriebenen Programm getestet :mrgreen: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:29 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz