AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

schnell Zeilen zählen

Ein Thema von akko5 · begonnen am 13. Dez 2007 · letzter Beitrag vom 13. Dez 2007
Antwort Antwort
akko5

Registriert seit: 13. Dez 2007
5 Beiträge
 
#1

schnell Zeilen zählen

  Alt 13. Dez 2007, 14:39
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:
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;
Dann hatte ich die Idee, die Datei in Blöcken auszulesen und dann die Zeilenumbrüche zu zählen.
Diese Methode arbeitet viel schneller, nur leider gibt sie immer (meistens) zuviele Zeilen zurück.
Delphi-Quellcode:
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;
Wäre nett, wenn sich das mal jmd. anschauen könnte und mir sagen kann, wieso der 2. Code nicht richtig funktioniert.

Danke!
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
39.647 Beiträge
 
Delphi 11 Alexandria
 
#2

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 14:51
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
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#3

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 14:54
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;
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
akko5

Registriert seit: 13. Dez 2007
5 Beiträge
 
#4

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 14:57
Zitat von himitsu:
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
Oh, Mann vielen Dank das habe ich nicht bedacht.

Danke

@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!
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.262 Beiträge
 
Delphi 2006 Professional
 
#5

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 15:02
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.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
KesseK

Registriert seit: 31. Jan 2007
Ort: Dortmund
48 Beiträge
 
Delphi 7 Personal
 
#6

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 15:22
Zitat von Christian Seehase:
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.
Hallo Christian, habe das gerade getestet und bin absolut nicht davon überzeugt. Auch große Dateien lassen sich damit schnell auslesen. Die Performance hielt sich auch gut.
Sammy
lalalalalalala la la .. Wem gehört das Reh auf der Wiese dort?
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
39.647 Beiträge
 
Delphi 11 Alexandria
 
#7

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 15:37
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.
BUFFER_SIZE = 65536; // 64 KB
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014
  Mit Zitat antworten Zitat
akko5

Registriert seit: 13. Dez 2007
5 Beiträge
 
#8

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 15:58
Zitat von himitsu:
@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.
BUFFER_SIZE = 65536; // 64 KB
Ja Danke. Hab auch noch einen kleinen Fehler gefunden, dass die letzte Zeile nicht immer gezählt wurde.
So sollte die Funktion perfekt sein:
Delphi-Quellcode:
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;
Für eine ~100 MB Textdatei (9105111 Zeilen) brauchte die Funktion 296 ms.

MfG
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
39.647 Beiträge
 
Delphi 11 Alexandria
 
#9

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 16:14
iRead, falls die Letzte Zeile nicht den Puffer ausfüllte
Delphi-Quellcode:
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);
direkt in den String eingelesen (PosCount/PosEx möchten das ja eh in einem String) und intern gezählt:
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;
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.262 Beiträge
 
Delphi 2006 Professional
 
#10

Re: schnell Zeilen zählen

  Alt 13. Dez 2007, 17:11
Moin Akko,

das ist ja mal ein interessantes Problem

Probier' das hier mal aus:

Delphi-Quellcode:
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;
Auf dem gleichen Rechner mit der gleichen Datei getestet, ist die noch einmal schneller als Deine
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
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:14 Uhr.
Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf