| Autor |
Beitrag |
Lestat
Hält's aus hier
Beiträge: 11
|
Verfasst: 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
      
Beiträge: 312
|
Verfasst: Di 25.03.03 14:40
|
|
Lestat 
Hält's aus hier
Beiträge: 11
|
Verfasst: Di 25.03.03 14:51
Danke !
hat alles geklappt !
|
|
Motzi
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: 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!
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 
Hält's aus hier
Beiträge: 11
|
Verfasst: Di 25.03.03 14:59
Ihr wisst bestimmt auch wie ich Char in einen String umwandeln kann, oder ?? 
|
|
ShadowCaster
      
Beiträge: 312
|
Verfasst: 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  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... 
Zuletzt bearbeitet von ShadowCaster am Di 25.03.03 15:13, insgesamt 2-mal bearbeitet
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Di 25.03.03 15:02
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 
Hält's aus hier
Beiträge: 11
|
Verfasst: Di 25.03.03 15:04
mist hab mich vertan ... ähh ... das war String in Char ...
wisst ihr das vielleicht auch ....
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Di 25.03.03 15:08
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
|
Verfasst: 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:
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
      
Beiträge: 312
|
Verfasst: 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
      
Beiträge: 312
|
Verfasst: 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.
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
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: 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?!?
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
      
Beiträge: 312
|
Verfasst: 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
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: 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..!
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:
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!! 
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
MathiasSimmack
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: 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
      
Beiträge: 1349
Erhaltene Danke: 1
Win XP
D5 Pers (SSL), D2005 Pro, C, C#
|
Verfasst: 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
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: 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
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: 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
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Di 25.03.03 21:23
Wow, mein drittes Posting hintereinander!
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!
|
|