Autor Beitrag
Lestat
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 25.03.03 14:28 
Hallo Leutz,

hab ne Problemstellung bekommen und weiß nicht wie ich anfangen soll.
Also,
ich soll einen Buchstaben eingeben in eine Datei reinschauen und zählen wir oft dieser Buchstabe in der Datei vorhanden ist!

danke
ShadowCaster
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 312



BeitragVerfasst: Di 25.03.03 14:40 
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
function zaehleBuchstabe(DateiName : String; Buchstabe : Char) : Integer;
var
  i : Integer;
  Ms1 : TMemoryStream;
  VergleichChar : Char;
begin
  Result := 0;
  Ms1 := TMemoryStream.Create;
  if FileExists(DateiName) then
  begin
    Ms1.LoadFromFile(DateiName);
    for i := 0 to Ms1.Size - 1 do
    begin
      Ms1.Read(VergleichChar, 1);
      if VergleichChar = Buchstabe then
        inc(Result);
    end;
  end;
  Ms1.Destroy;
end;


Das ist die Funktion, um den Buchstaben zu zählen. Ist eigentlich sehr einfach. Ich habs über einen Memorystream gemacht, weil das am einfachsten und schnellsten geht, solange die Datei nicht über 100 MB groß ist ;)

Um die Funktion zu verwenden, benutzt du folgenden Code:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm1.Button1Click(Sender: TObject);
var
  BuchstabenCount : Integer;
begin
  BuchstabenCount := zaehleBuchstabe('C:\Beispiel.dat', 'A')
end;


jetzt steht in der Variablen BuchstabenCount die Anzahl wie oft der Buchstabe in der Datei 'C:\Beispiel.dat' vorkommt.
Lestat Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 25.03.03 14:51 
Danke !

hat alles geklappt !
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 25.03.03 14:52 
Erstens würde ich das ganze direkt mit dem File machen und nicht über einen MemoryStream und außerdem nicht immer nur ein einzelnes Char lesen! Dann gehts auch schneller! :wink:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
function CountChars(const aFileName: String; c: Char): Integer;
const
  MAX = 10240;
var
  aStream: TFileStream;
  szBuffer: array [1..MAX] of Char;
  i, iReadCount: Integer;
begin
  Result := 0;
  aStream := TFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite);
  try
    while aStream.Position < aStream.Size do
    begin
      iReadCount := aStream.Read(szBuffer, MAX);
      for i := 1 to iReadCount do
        if szBuffer[i] = c then
          Inc(Result);
    end;
  finally
    aStream.Free;
  end;
end;


@ShadowCaster: Zum Freigeben eines Objektes sollte übrigens Free verwendet werden! Free überprüft noch explizit ob der Self-Pointer nil ist bevor es Destroy aufruft. Dadurch wird ein EAccessViolent verhindert, wenn versucht wird ein nicht mehr existierendes Objekt freizugeben.

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
Lestat Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 25.03.03 14:59 
Ihr wisst bestimmt auch wie ich Char in einen String umwandeln kann, oder ?? :?
ShadowCaster
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 312



BeitragVerfasst: Di 25.03.03 15:00 
Zitat:
Erstens würde ich das ganze direkt mit dem File machen und nicht über einen MemoryStream und außerdem nicht immer nur ein einzelnes Char lesen! Dann gehts auch schneller!


Das ist definitiv falsch! Wenn man so wie du zeichenweise einliest (Filestream - direkt von der Platte), dann dauert es doppelt bis dreifach so lang als wenn du erst komplett von der Platte in den Ram einliest und dann Zeichen für Zeichen bearbeitest. Ich hab das mal getestet und bei mir schneidet memorystream besser ab. Außerdem: wer verwendet noch filestream? Wenn schon Blockread, oder? egal

Zitat:

Zum Freigeben eines Objektes sollte übrigens Free verwendet werden! Free überprüft noch explizit ob der Self-Pointer nil ist bevor es Destroy aufruft. Dadurch wird ein EAccessViolent verhindert, wenn versucht wird ein nicht mehr existierendes Objekt freizugeben.


das war mir bisher unbekannt. Die Delphileute mit denen ich bisher geredet hab, sagten mir immer, dass ein Destroy auch ein Free aufruft intern... :?: :?: Naja, egal.

Allerdings ist die Lösung mit dem Memorystream leichter verständlich, würde ich mal behaupten 8) für einen Programmieranfänger.

Achja, du solltest auch wissen, dass eine While-Schleife langsamer ist als eine For-Schleife. Wenn du kannst, dann verwende gerade bei Dateneinleseroutinen immer Forschleifen.

Wenn du eine TList mit Records von hinten ab aufräumen willst, dann ist das was anderes, dann gilt eine Whileschleife.

Und eine ganze Einleseroutine in einen Try-Except-Block zu packen... uff... :shock:


Zuletzt bearbeitet von ShadowCaster am Di 25.03.03 15:13, insgesamt 2-mal bearbeitet
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 25.03.03 15:02 
ausblenden Quelltext
1:
2:
3:
4:
5:
var
  c : Char;
begin
  c := 's';
  Showmessage(String(c));

Den Cast mit String kannst du auch weglassen. Ich würde ihn nur machen, damit es etwas deutlicher wird, was passiert.
Lestat Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 25.03.03 15:04 
mist hab mich vertan ... ähh ... das war String in Char ... :oops:


wisst ihr das vielleicht auch ....
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 25.03.03 15:08 
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
var
  s : String
  c : Char;
begin
  s := 'Hello world';
  c := s[1]

Einfach den Index des Zeichens im String angeben. Das erste Zeichen hat den Index eins.
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 25.03.03 15:13 
ShadowCaster hat folgendes geschrieben:
Zitat:
Erstens würde ich das ganze direkt mit dem File machen und nicht über einen MemoryStream und außerdem nicht immer nur ein einzelnes Char lesen! Dann gehts auch schneller!


Das ist definitiv falsch! Wenn man so wie du zeichenweise einliest (Filestream - direkt von der Platte), dann dauert es doppelt bis dreifach so lang als wenn du erst komplett von der Platte in den Ram einliest und dann Zeichen für Zeichen bearbeitest. Ich hab das mal getestet und bei mir schneidet memorystream besser ab. Außerdem: wer verwendet noch filestream? Wenn schon Blockread, oder? egal

ShadowCaster, wann lernst du endlich richtig und aufmerksam zu lesen? Motzi hat nichts anderes gesagt als du:
Motzi hat folgendes geschrieben:
... und außerdem nicht immer nur ein einzelnes Char lesen! Dann gehts auch schneller!

Und wenn du dir mal seinen Beispielcode anschaust, müsstest du eigentlich erkennen, dass max. 10.240 Zeichen gelesen werden (können). Die tatsächlich gelesenen Bytes (bzw. Chars) werden als Ergebnis von "Read" zurückgegeben:
ausblenden Quelltext
1:
2:
3:
4:
5:
const
  MAX = 10240;
{ ... }
      iReadCount := aStream.Read(szBuffer, MAX);
{ ... }

Der große Vorteil bei dieser Methode ist der, dass du auch fette Dateien (mit Größen von 10meg aufwärts) relativ flott (weil: stückchenweise) bearbeiten kannst. Bei deinem Memorystream hast du so ein dickes Monster komplett im RAM. Ob das so gut ist ...
ShadowCaster
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 312



BeitragVerfasst: Di 25.03.03 15:15 
@MathiasSimmack: Wenn du davon ausgehst, dass ein Filestream Zeichen für Zeichen einliest, dann hat motzi was anderes gesagt wie ich! Wenn ich Blockweise einlese aus einem Filestream, dann ist das was anderes.
ShadowCaster
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 312



BeitragVerfasst: Di 25.03.03 15:49 
Hier ist eine Methode wie ich die Aufgabe gelöst hätte. Ich glaub, schneller dürfte es nicht mehr gehen. Wenn man den Buffer 2 MB groß macht, dann liest man mindestens doppelt so schnell wie der Windows-Explorer. 1MB reicht jedoch auch.

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
function zaehleZeichen(DateiName : String; Buchstabe : Char) : Integer;
var
  i : Integer;
  InFile : File;
  InBuffer : PChar;
  Gelesen : Integer;
  InPos : Pointer;
const
  BufferSize = 1048576;
begin
  Result := 0;
  if FileExists(DateiName) then
  begin
    GetMem(InBuffer, BufferSize);
    AssignFile(InFile, DateiName);
    Reset(InFile, 1);
    repeat
      BlockRead(InFile, InBuffer^, BufferSize, Gelesen);
      InPos := Pointer(InBuffer);
      for i := 0 to Gelesen - 1 do
      begin
        if Char(InPos^) = Buchstabe then
          inc(Result, 1);
        InPos := Pointer(Cardinal(InPos) + 1);
      end;
    until (Gelesen < BufferSize);
    FreeMem(InBuffer);
    CloseFile(InFile);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(IntToStr(zaehleZeichen('C:\test.txt', 'b')));
end;


wenn jetzt noch einer was zu meckern hat, dann weiß ich auch nicht mehr.

Die Funktion funzt bei mir einwandfrei und auf meinem 1,8 Ghz-Rechner würde der bei einer 1Gigabyte-Datei die Anzahl der A's in weit weniger als einer Minute ermitteln. Aber zum Testen hab ich keinen Bock ;)
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 25.03.03 18:06 
@ShadowCaster: Ein MemoryStream ist ja nichts anderes als ein "Workaround" um einen Datenblock im Speicher. Preisfrage: Was glaubst du wie die Methode LoadFromFile die Daten von der Festplatte in den Speicher bekommt?!? :wink:

Edit: Übrigens wegen deinem Kommentar zum FileStream und weil ich gerade gesehen habe, dass du AssignFile verwendest. Dann will ich an dieser Stelle mal Hagen Reddmann, einen Spezialisten aus dem Entwickler Forum zitieren:
Hagen Reddmann hat folgendes geschrieben:
Streams sind logischer, komfortabler, Objecte, kompatible untereinander, einfacher und alle guten VCL Komponenten und Objecte sollten Streams unterstützen. Sie sind nicht langsammer als BlockRead aber meistens schneller, da man ja Spezialstreams nutzen kann die intern automatisch Dateizugriffe cachen, völlig transparent für Dich. Streams sind die von Borland propagierte und unterstützte Methode !!

Streams benötigen aber einen höheren Overhead, da die Unit Classes mit eingebunden wird, Streams sind Objectorientiert also ein Schwachpunkt für denjenigen der nur prozedural programmieren kann.

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
ShadowCaster
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 312



BeitragVerfasst: Di 25.03.03 18:46 
streams verwenden vielleicht teilweise den Blockread, das ist richtig aber sie sind nicht schneller. Wenn ich doch nur den Link auf diese eine Statistikseite hätte.. ich such nochmal. Da gibt es sehr große Unterschiede und die darf man nicht unterschätzen.

Hast du dir mal die asm-Datei vom Blockread angeschaut? Das sind ein paar wenige Assembler-Befehle für den Inline-Assembler. Streams haben meistens eine Klasse als Overhead und es werden Variablen durch Funktionsaufrufe geparst.

Das ist vergleichbar wenn du eine Datenbank-Query mit oder ohne Overhead benutzt. Im Endeffekt hat die CPU (wenn auch wenige Befehle), jedoch einiges mehr abzuarbeiten, wenn du Streams benutzt. Glaub mir, ich weiß wovon ich rede, wenn ich TFileStream nicht verwende ;)

Werd mal schauen, ob ich den Link finde...

Soll jetzt kein Streitgespräch geben, ok? ich werd mal nachschauen.
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 25.03.03 19:09 
ShadowCaster hat folgendes geschrieben:
Soll jetzt kein Streitgespräch geben, ok?

War auch nie meine Absicht! Aber für eine sachliche Diskussion bin ich immer zu haben..! :wink:

Zu BlockRead kann ich nicht viel sagen, da ich es (aus oben genannten Gründen) schon lange nicht mehr verwende.
Zum MemoryStream: die LoadFromFile Methode tut auch nichts anderes als einen TFileStream zu verwenden:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TMemoryStream.LoadFromFile(const FileName: string);
var
  Stream: TStream;
begin
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(Stream);
  finally
    Stream.Free;
  end;
end;


Und zu TFileStream: TFileStream ist nur eine ableitung eines THandleStreams und dieser ist wiederum nur ein Workaround um die API-Datei-Funktionen...

Zu den Statistiken: würd mich interessieren! Bitte posten falls du den Link findest! Und nachdem du mich jetzt neugierig gemacht hast werd ich selber auch mal testen!! :wink:

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 25.03.03 19:45 
ShadowCaster hat folgendes geschrieben:
@MathiasSimmack: Wenn du davon ausgehst, dass ein Filestream Zeichen für Zeichen einliest, dann hat motzi was anderes gesagt wie ich! Wenn ich Blockweise einlese aus einem Filestream, dann ist das was anderes.

10.240 Zeichen auf einen Schlag ist, IMHO, ein Block. Und damit hat Motzi nichts anderes gesagt als du. Aber ich merke, du bist lernfähig. :)

btw: Ich habe auch einen kleinen Test gemacht. Meine Datei hatte allerdings eine Größe von 90.882.573 Bytes (~90meg) hat. Interessanterweise lag der Filestream (mit einem 128k Puffer) mit durchschnittlich 13 Sekunden vor dem Memorystream mit durchschnittlich 19 Sekunden. Gefunden haben beide die 349.583 A's.
Aber das muss nichts heißen. Mit einer anderen Systemkonfiguration oder auch mit einer anderen Datei könnte das Ergebnis durchaus anders aussehen.
wulfskin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1349
Erhaltene Danke: 1

Win XP
D5 Pers (SSL), D2005 Pro, C, C#
BeitragVerfasst: Di 25.03.03 19:46 
Hallo ShadowCaster!

Diese Statistiksetie interessiert mich auch! Im alten DF habe ich auch mal gepostet, dass ein BlockRead schneller ist, da ich es mit TFIleStream verglichen habe. Am Ende war es so, das TMemoryStream schneller als BlockRead war.
Also ich sage BlockRead ist so schnell wie Streams. Aber dafür sind Streams einfacher!

Gruß wulfskin!!

_________________
Manche antworten um ihren Beitragszähler zu erhöhen, andere um zu Helfen.
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 25.03.03 20:36 
Ich hab mir jetzt mal die Sourcen vom TMemoryStream angeschaut. Es wird zuerst der entsprechende Speicher am Heap reserviert und dann die ganze Datei in einem Schub eingelesen. Nachteil bei der Sache: bei einer sehr großen Datei landet diese beim einlesen sofort wieder in der Swap-Datei.

Und warum ich der Meinung bin, dass ein MemoryStream in diesem Fall eher ungeeignet ist. Sofern die Daten nicht in der Swap-Datei liegen, kann man auf Daten im RAM zwar schneller zugreifen als von der Platte, aber: Zuerst wird die Datei von der Festplatte in den Speicher geladen (erstes mal Lesen). Dann werden die Daten aus dem MemoryStream ausgelesen und durchgecheckt (2tes mal Lesen). 2 mal Lesen.. irgendwie is das also doppelt gemoppelt...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 25.03.03 20:52 
Also.. ich hab jetzt mal getestet und bei mir sind FileStreams und AssignFile ungefähr gleich schnell.. ist auch logisch, weil ich hab mir BlockRead mal im CPU-Fenster angeschaut und das macht im Prinzip nichts anderes als der FileStream (genau genommen der THandleStream) nämlich ReadFile aus der Kernel32.dll aufzurufen...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 25.03.03 21:23 
Wow, mein drittes Posting hintereinander! :wink:

Hab jetzt den Test auch noch mit TMemoryStream gemacht.. (LoadFromFile und dann dieselbe Suchroutine) Und das Ergebnis hat mich nicht wirklich überrascht! Mit dem TMemoryStream war das ganze um bis zu 18x langsamer!

Getest hab ich das ganze mit einer 50MB Datei...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!