|
Antwort |
Registriert seit: 4. Dez 2013 47 Beiträge |
#1
Hallo liebe Delphianer,
ich bin so gut wi neu hier, habe Euch durch Stöbern im Internet gefunden und auch schon hier im Forum ein wenig geblättert. Leider programmiere ich noch nicht allzu lange. Ich möchte es aber gerne lernen und da habe ich schon das erste Prorblem, das ich mir nicht erklären kann. Ich habe diese Unit geschrieben.
Delphi-Quellcode:
Bei Anwendung dieser Unit erhalte ich aber einen Fehler:
unit UByteStream;
interface uses Sysutils, Classes; type PByteStream = ^TByteStream; TByteStream = Class(TObject) private FFile: File Of Byte; FFileName: String; public constructor Create(AFileName: String; Mode: Word); destructor Destroy; override; function ReadByte: Byte; function Read(var Buffer: array of byte; BufSize: Longint): Longint; procedure WriteByte(b: Byte); function Write(var Buffer: array of byte; BufSize: Longint): Longint; function Position: Longint; function Size: Longint; procedure Seek(APos: Longint; Flag: Word); End; implementation { TByteStream } constructor TByteStream.Create(AFileName: String; Mode: Word); var IResult: Integer; begin inherited Create; System.Assign(FFile, FFileName); {$I-} if (Mode=fmOpen) or (Mode=fmOpenRead) then begin System.Reset(FFile); IResult := System.IOResult; if IResult <> 0 then fail; end else if Mode = fmCreate then begin System.Rewrite(FFile); IResult := IOResult; if IResult <> 0 then fail; end; {$I+} end; destructor TByteStream.Destroy; begin System.Close(FFile); inherited Done; end; function TByteStream.ReadByte: Byte; var b: Byte; begin System.Read(FFile, b); Result := b; end; function TByteStream.Read(var Buffer: array of byte; BufSize: Longint): Longint; var Count,Current,BufStart,BufEnd,BufPos: Word; begin BufStart := Low(Buffer); BufEnd := High(Buffer); BufPos := BufStart; Current := System.FilePos(FFile); Count := Current; while Count < Current + BufSize do begin System.Read(FFile, Buffer[BufPos]); inc(BufPos); inc(Count); end; Result := Count - Current; end; procedure TByteStream.WriteByte(b: Byte); begin System.Write(FFile, b); end; function TByteStream.Write(var Buffer: array of byte; BufSize: Longint): Longint; var Count,Current,BufStart,BufEnd,BufPos: Word; x: array[0..1023] of byte absolute Buffer; begin BufStart := Low(Buffer); BufEnd := High(Buffer); BufPos := BufStart; Current := System.FilePos(FFile); Count := Current; while Count < Current + BufSize do begin System.Write(FFile, Buffer[BufPos]); inc(BufPos); inc(Count); end; Result := Count - Current; end; function TByteStream.Position: Longint; begin Result := System.FilePos(FFile); end; function TByteStream.Size: Longint; begin Result := System.FileSize(FFile); end; procedure TByteStream.Seek(APos: Longint; Flag: Word); begin case Flag of soFromBeginning: System.Seek(FFile, APos); soFromCurrentPos: ; soFromEnd: ; end; end; end. "Datei nicht geöffnet". Warum ist das so?
Delphi-Quellcode:
Entweder ich bin betriebsblind und habe was entscheidendes übersehen oder eine Datei kann in einer Klasse nicht geöffnet werden.
type
TReader = class(TObject) private MeineBytes: TByteStream; public constructor Create(Filename: String); destructor Destroy; override; procedure ReadBytes; end; implementation constructor Create(Filename: String); begin inherited Create; MeineBytes := TByteFile.Create(FileName, fmOpen); end; destructor Destroy; begin MeineBytes.Free; inherited Destroy; end; procedure ReadBytes; begin //hier sollten die Bytes aus der Datei nun gelesen werden //Aber die Datei ist nicht geöffnet end; end. Wenn letzteres zutrifft, warum ist das so. Ich möchte die Zusammenhänge gerne verstehen. Wer kann mir helfen? |
Zitat |
FragenderHerbert |
Öffentliches Profil ansehen |
Mehr Beiträge von FragenderHerbert finden |
Registriert seit: 5. Mai 2008 Ort: Oberschwaben 1.275 Beiträge Delphi 11 Alexandria |
#2
AFileName wird übergeben aber nichts damit gemacht, stattdessen wird FFilename benutzt, das nirgends gesetzt wurde.
Delphi-Quellcode:
constructor TByteStream.Create(AFileName: String; Mode: Word);
var IResult: Integer; begin inherited Create; System.Assign(FFile, FFileName); Hinter dir gehts abwärts und vor dir steil bergauf ! (Wolfgang Ambros)
|
Zitat |
(Moderator)
Registriert seit: 9. Dez 2005 Ort: Heilbronn 39.858 Beiträge Delphi 11 Alexandria |
#3
Da ein Klassentyp eine Referenz( Zeiger-)typ ist, wird kein Zeigertyp benötigt
PByteStream = ^TByteStream; Die Angabe der Superklasse TObject kann enfallen TByteStream = Class(TObject) da beim Weglasen automatisch von TObject geerbt wird. Ich würde hier aber direkt von TStream ableiten
Markus Kinzler
|
Zitat |
Registriert seit: 4. Dez 2013 47 Beiträge |
#4
Hallo,
danke erst mal für Eure wirklich schnellen Antworten. @baumina: Habe das geändert und jetzt wird die Datei geöffnet. @mkinzler: Ich werde in Zukunft auch von TStream ableiten. Aber vorher schau ich mir den Quelltext (Turbo Delphi) von TStream an. Da gibt es bereits jetzt jede Menge Fragen. Ich glaub, ich schau mir das in aller Ruhe erst mal an. Was ich bis jetzt gesehen habe, ist, das das Stream Objekt auch aus einer gewöhnlichen Datei liest. Bleibt die Frage, wie Objekte gelesen und geschrieben werden. Da werd ich wohl noch einige Zeit brauchen, um das zu verstehen. Will mich erst mal damit beschäftigen. |
Zitat |
FragenderHerbert |
Öffentliches Profil ansehen |
Mehr Beiträge von FragenderHerbert finden |
Registriert seit: 5. Jan 2005 Ort: Stadthagen 9.454 Beiträge Delphi 10 Seattle Enterprise |
#5
Mal so als Frage unter uns Kegelschwestern
Was unterscheidet deine Klasse jetzt von einem TFileStream? (Abgesehen davon, dass TFileStream von TStream abgeleitet ist) Oder was erwartest du von deiner Klasse, was nicht mit TFileStream erledigt werden könnte? Oder ganz ketzerisch gefragt, was denkst du, was jemand erwartet, wenn er TByteStream liest? Einen Stream wo Bytes drin sind? (das mit dem Dateinamen würde mich aber schon wieder überraschen - warum dann nicht TFileByteStream ?) Ja was ist denn dann in einem TStream? (auf jeden Fall keine Hasenköttel - ich dachte immer, da sind Bytes drin)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60) |
Zitat |
Registriert seit: 28. Apr 2008 Ort: Stolberg (Rhl) 6.659 Beiträge FreePascal / Lazarus |
#6
@Sir Rufo
vielleicht wollte er einfach mal üben? "learning by doing" Gruß K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector |
Zitat |
Registriert seit: 11. Okt 2003 Ort: Elbflorenz 44.063 Beiträge Delphi 12 Athens |
#7
OK, dann halt zum Üben....
Im Prinzip wurde hier einfach nur ein "File Of Byte" weggekapselt, insofern ist das schon OK. Auch wenn da TFileStream und Co. wohl eine bessere Alternative wären. TByteStream ist wohl kein ganz guter Name, da es ja nicht von TStreams abgeleitet ist und sonst auch kein "Stream" intern verwendet wird, bzw. es auch nur irgendwas damit zu tun hat. Wie wäre es TBytesFile? Wieso wird bei Write der Puffer zum Schreiben übergeben? (Var-Parameter) An dem Buffer soll doch nichts verändert werden. Wozu brauchst du das BufSize? Die länge steht doch schon im Array drin. (Length) Einem Array-Parameter kann man zwar schön statische Arrays über geben, oder ein idrekt definiertes Array (weiß jetzt nicht wie das heißt). x.Write([1, 2, 3, 4]);
Delphi-Quellcode:
Was sind fail und done?
procedure Write(Buffer: array of byte); overload; // für statische Arrays und direkte Angaben
procedure Write(Buffer: TBytes); overload; // für dynamische Arrays procedure Read(var Buffer: TBytes; Count: Longint); // nur für dynamische Arrays möglich // oder function Read(Count: Longint): TBytes; // ebenso Wenn du kein Delphi, sondern Lazarus/FPC verwendest, dann erwähn das bitte auch. Aber dennoch ... wieso inherited Destroy eigentlich Done und nicht das übergeordnete Destroy? Die Variable IResult kann weg, da du den Wert eh nur einmal auswertest und das gleich in der nächsten Zeile. Was passiert, wenn Mode "ungültig" ist, also keinem deiner behandelten Werte entspricht? Wieso "verwirfst" du quasi das IOResult? Das gehört an fail übergeben, oder es sollte man sonstwie auswerten und weiterreichen .... und was macht fail eigentlich? Der Grund, also IOResult, gehört, wenn möglich übersetzt, in die Fehlermeldung mit rein, damit man weiß warum es nicht ging. Da du die Datei komplett kapselst, gehört auch die Fehlerbehandlung koplett da rein. (ja, IOResult könnte man extern eventuell noch abfragen, aber jenachdem was fail macht, könnte das dann schon ungültig sein) Nochmal zu den "unnötigen" Variablen, wie vorhin schon das IResult.
Delphi-Quellcode:
Da das Result von TByteStream.Read immer dem BufSize entspricht, ist es nutlos und es kann eine Prozedur werden.
function TByteStream.ReadByte: Byte;
begin System.Read(FFile, Result); end; TByteStream.Read(Buffer, BufSize) : entweder du nimmst die größe von Buffer (Length) oder den Wert von BufSize. - bei BufSize wird der Buffer auf die "richtige" länge gebracht und nicht blind drin rumgeschrieben - bei Length(Buffer) ist BufSize "nutzlos" und flieg raus, aus dem Code Und wozu Read/Write das Current/FilePos brauchen, hab ich nicht verstanden, danie wirklich verwendet wird, außer es im Result wieder rauszurechnen ... hätte man bei 0 angefangen, bräuchte man es auch nicht rausrechnen, bzw. -0 ist ja nichts. Warum macht Seek bei 2zwei Drittel des Codes Nichts? (CurrentPos und End) Wenn du schon OpenRead und Open aka OpenReadWrite unterscheidest, dann gehort vor das Reset noch der richtige FileMode zugewiesen. TReader ist ein blöder Name ... Was liest der denn und außerdem TReader? Wie wäre es mit TBytesReader, für das TBytesFile? Und daß beim TReader in der Implementation der klassenname fehlt ist bestimmt nur schlimmes Copy&Paste.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat, wird PoSex im Delphi viel seltener praktiziert. Geändert von himitsu (13. Dez 2013 um 14:28 Uhr) |
Zitat |
Registriert seit: 4. Dez 2013 47 Beiträge |
#8
@Sir Rufo:
Ja das ist zum Üben gedacht. Ich möchte verstehen, wie ein echter Stream intern funktioniert. Den Quelltext aus der Unit Classes verstehe ich noch nicht gut genug dazu. Eigene Experimente geben mir da mehr Aufschluss.
Zitat:
OK, dann halt zum Üben....
Im Prinzip wurde hier einfach nur ein "File Of Byte" weggekapselt, insofern ist das schon OK. Auch wenn da TFileStream und Co. wohl eine bessere Alternative wären. TByteStream ist wohl kein ganz guter Name, da es ja nicht von TStreams abgeleitet ist und sonst auch kein "Stream" intern verwendet wird, bzw. es auch nur irgendwas damit zu tun hat. Wie wäre es TBytesFile? Wie gesagt, ich will die Funktionsweise des Streams verstehen lernen. Aber für meine Klasse wäre natürlich TBytesFile ein besserer Name.
Zitat:
Wieso wird bei Write der Puffer zum Schreiben übergeben? (Var-Parameter)
An dem Buffer soll doch nichts verändert werden. Ok, das kann ich ändern. Allerdings gab es in Turbo Pascal, von wo ich das nach Delphi portiert habe, noch keine const Parameter. Heute in Delphi kann man lt Handbuch schreiben: procedure Write(Const Buffer; Size: Longint); in Turbo Pascal noch keine const Parameter, ging es nur so: procedure Write(var Buffer; Size: Longint);
Zitat:
Wozu brauchst du das BufSize?
Die länge steht doch schon im Array drin. (Length)
Zitat von himitsu:
Einem Array-Parameter kann man zwar schön statische Arrays über geben, oder ein idrekt definiertes Array (weiß jetzt nicht wie das heißt).
x.Write([1, 2, 3, 4]);
Zitat von himitsu:
markieren
Delphi-Quellcode: procedure Write(Buffer: array of byte); overload; // für statische Arrays und direkte Angaben
Zitat von himitsu:
procedure Write(Buffer: TBytes); overload; // für dynamische Arrays
Zitat von himitsu:
procedure Read(var Buffer: TBytes; Count: Longint); // nur für dynamische Arrays möglich
Außerdem kann ich doch an ein Open Array, wie das in Turbo Pascal hieß, ein beliebig großes Array übergeben. Wenn ich dann aber aus diesem Array nicht alle Bytes lese/schreibe, wenn also Count kleiner ist als die Größe des Arrays? (Bis gleich groß sollte ja auch noch klappen).
Zitat von himitsu:
// oder
function Read(Count: Longint): TBytes; // ebenso
Zitat von himitsu:
Was sind fail und done?
Zitat von himitsu:
Wenn du kein Delphi, sondern Lazarus/FPC verwendest, dann erwähn das bitte auch.
Zitat von himitsu:
Aber dennoch ... wieso inherited Destroy eigentlich Done und nicht das übergeordnete Destroy?
type TMeinObjekt = Object in Delphi type TMeinObjekt = class Der Constructor hieß dort Init, der Destructor hieß Done. Ich denke, im Interesse guten Programmierstils sollte man diese Namensgebung beibehalten und erst bei den Delphi Klassen stattdessen Create und Destroy verwenden. Aber beim Portieren habe ich wohl vergessen, das Done abzuändern. Richtig muss es heißen: inherited Destroy. Habe das nicht korrekt portiert.
Zitat von himitsu:
Die Variable IResult kann weg, da du den Wert eh nur einmal auswertest und das gleich in der nächsten Zeile.
Was passiert, wenn Mode "ungültig" ist, also keinem deiner behandelten Werte entspricht?
Delphi-Quellcode:
...
if mode=fmCreate then begin end else fail; //weil ha bei ungültigem Mode die Datei nicht geöffnet wird
Zitat von himitsu:
Wieso "verwirfst" du quasi das IOResult?
Das gehört an fail übergeben, oder es sollte man sonstwie auswerten und weiterreichen .... und was macht fail eigentlich? Der Grund, also IOResult, gehört, wenn möglich übersetzt, in die Fehlermeldung mit rein, damit man weiß warum es nicht ging. Da du die Datei komplett kapselst, gehört auch die Fehlerbehandlung koplett da rein. (ja, IOResult könnte man extern eventuell noch abfragen, aber jenachdem was fail macht, könnte das dann schon ungültig sein) Hmmm, IOResult an fail übergeben??? Da muss ich wohl moch mal meine alten Turbo Pascal Handbücher rausholen. Ich dachte Fail sei eine Prozedur, die ich im Konstruktor aufrufen kann, wenn die Erzeugung meines Objektes fehl schlägt.
Zitat von himitsu:
Nochmal zu den "unnötigen" Variablen, wie vorhin schon das IResult.
markieren Delphi-Quellcode: function TByteStream.ReadByte: Byte; begin System.Read(FFile, Result); end; Da das Result von TByteStream.Read immer dem BufSize entspricht, ist es nutlos und es kann eine Prozedur werden.
Zitat von himitsu:
TByteStream.Read(Buffer, BufSize) : entweder du nimmst die größe von Buffer (Length) oder den Wert von BufSize.
- bei BufSize wird der Buffer auf die "richtige" länge gebracht und nicht blind drin rumgeschrieben - bei Length(Buffer) ist BufSize "nutzlos" und flieg raus, aus dem Code
Zitat von himitsu:
Und wozu Read/Write das Current/FilePos brauchen, hab ich nicht verstanden, danie wirklich verwendet wird, außer es im Result wieder rauszurechnen ... hätte man bei 0 angefangen, bräuchte man es auch nicht rausrechnen, bzw. -0 ist ja nichts.
Warum macht Seek bei 2zwei Drittel des Codes Nichts? (CurrentPos und End)
Zitat von himitsu:
Weil diese Methode noch nicht fertig implementiert ist. In Turbo Pascal nimmt die Prozedur Seek und auch die Stream Methode Seek nur die gewünschte Position entgegen, ich Delphi kann man noch angeben, ob vom Beginn des Streams, soFromBeginning, von der aktuellen Position aus, soFromCurrent oder vom Streamende aus soFromEnd gesucht werden soll. Das möchte ich implementieren.
Wenn du schon OpenRead und Open aka OpenReadWrite unterscheidest, dann gehort vor das Reset noch der richtige FileMode zugewiesen. Habe den Quellcode von Turbo Pascal aus nach Delphi übersetzt. Delphi besitze ich erst seit kurzer Zeit.
Zitat von himitsu:
TReader ist ein blöder Name ... Was liest der denn und außerdem Delphi-Referenz durchsuchenTReader?
Wie wäre es mit TBytesReader, für das TBytesFile?
Zitat von himitsu:
Und daß beim TReader in der Implementation der klassenname fehlt ist bestimmt nur schlimmes Copy&Paste.
Und wie kommen Objekte in die Datei? TStream, der echte Stream ist doch erst mal eine Klasse, in der etwas auf die externen Datenträger geschrieben wird. Das kann erst mal so weit mit gewöhnlichen Dateifunktionen passieren. Aber wie kommt nun ein Objekt in so eine Datei? Wie gesagt, ist mir der Quelltext aus der Unit Classes da noch nicht klar zumal dort in TStream + Nachfolgern bereits Methoden aufgerufen werden, die wohl in anderen Units und anderen Klassen verborgen sind. In der Unit Classes lese ich zum Beispiel das hier:
Delphi-Quellcode:
Wenn ich dann weiter schaue, finde ich zunächst das hier:
function TStream.ReadComponent(Instance: TComponent): TComponent;
var Reader: TReader; begin Reader := TReader.Create(Self, 4096); try Result := Reader.ReadRootComponent(Instance); finally Reader.Free; end; end; procedure TStream.WriteComponent(Instance: TComponent); begin WriteDescendent(Instance, nil); end;
Delphi-Quellcode:
Da weiß ich aber immer noch nicht, wie WriteComponent intern arbeitet.
procedure TWriter.WriteDescendent(Root: TComponent; AAncestor: TComponent);
begin FRootAncestor := AAncestor; FAncestor := AAncestor; FRoot := Root; FLookupRoot := Root; WriteSignature; WriteComponent(Root); end; Aber hier wird es jetzt interessant:
Delphi-Quellcode:
Das möchte ich jetzt gerne verstehen.
procedure TWriter.WriteComponent(Component: TComponent);
function FindAncestor(const Name: string): TComponent; var I: Integer; begin for I := 0 to FAncestorList.Count - 1 do begin Result := FAncestorList[I]; if SameText(Result.Name, Name) then Exit; end; Result := nil; end; var OldAncestor: TPersistent; OldRootAncestor: TComponent; AncestorComponent: TComponent; I: Integer; begin OldAncestor := Ancestor; OldRootAncestor := RootAncestor; try Include(Component.FComponentState, csWriting); for I := 0 to Component.ComponentCount - 1 do if csSubComponent in Component.Components[I].ComponentStyle then Include(Component.Components[I].FComponentState, csWriting); if Assigned(FAncestorList) then Ancestor := FindAncestor(Component.Name); if Assigned(FOnFindAncestor) and ((Ancestor = nil) or (Ancestor is TComponent)) then begin AncestorComponent := TComponent(Ancestor); FOnFindAncestor(Self, Component, Component.Name, AncestorComponent, FRootAncestor); Ancestor := AncestorComponent; end; Component.WriteState(Self); Exclude(Component.FComponentState, csWriting); for I := 0 to Component.ComponentCount - 1 do if csSubComponent in Component.Components[I].ComponentStyle then Exclude(Component.Components[I].FComponentState, csWriting); finally Ancestor := OldAncestor; FRootAncestor := OldRootAncestor; end; end; Schreibt diese Methode die Eigenschaften und Datenfelder in den Stream? Dann müßte bei Instantiierung ein Konstruktor aufgerufen werden, nachdem die Felder der Klasse vom Reader belegt worden sind. Das hier ist ja die Methode WriteComponent aus TWriter, offenbar wird zum Schtreiben eine Klasse verwendet, die halt das Objekt nur in den Stream schreiben kann, zum Lesen gibt es dann eine andere Klasse. Aber bei TReader.ReadComponent() wird es richtig umfangreich. Da blick ich derzeit noch gar nicht durch. Da gibt es in der Methode jede Menge lokale Prozeduren, die verstanden sein wollen. Und dann ist da immer noch der Code der eigentlichen Methode. . Geändert von FragenderHerbert (13. Dez 2013 um 19:40 Uhr) |
Zitat |
FragenderHerbert |
Öffentliches Profil ansehen |
Mehr Beiträge von FragenderHerbert finden |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |