Dynamische Datenstruktur einlesen

Hallo,

ich bin dabei für mein Studium Pascal zu lernen, aber bei den letzten Aufgaben bekomme ich Probleme, die ich alleine nicht mehr hinbekomme.

Es geht darum Verkaufsmengen und Preise aus einer gegebenen .dat Datei einzulesen. Da es in den folgenden Teilaufgaben darum geht Elemente dieser Liste hinzuzufügen, handelt es sich wohl um eine dynamische Datenstruktur.

Ziel des Programms soll erst einmal nur sein die Daten der .dat Datei auf dem Bildschirm auszugeben. Dabei ist bekannt, dass die Mengen ganzzahlig und die Preise in Euro gespeichert sind, was wohl Rückschlüsse auf deren Datentypen (Integer und Real) gibt.

Bei Interesse könnte ich die .dat Datei, sowie die Musterlösung als .exe Programm per eMail verschicken.

Hier dann erstmal mein Versuch. Der Compiler findet zwar keinen Fehler, aber bei Ablauf des Programmes erfolgt keinerlei Ausgabe:

PROGRAM einlesen;
USES crt;

CONST FILENAME = ‚C:\absatz.dat‘;

type
PVListe = ^Vliste;
Vliste = record
menge:integer;
preis:Real;
next:stuck_out_tongue:Vliste;
end;
var
f: FILE of PVliste;
l: PVliste;

BEGIN
ASSIGN (f, FILENAME);
RESET (f);
while lnil do
begin
READ (f,l);
WRITELN (‚Die Menge beträgt: ‚,l^.menge,‘‘);
WRITELN (‚Der Tagespreis beträgt: ‚,l^.preis:1:2,‘‘);
l:=l^.next;
end;
CLOSE (f);
READKEY;
END.

Vielen Dank,

Marc Borsos

Hallo Marc,

beim nur flüchtigen Drübergucken sind mir folgende beiden Fehler aufgefallen:

PROGRAM einlesen;
USES crt;

CONST FILENAME = ‚C:\absatz.dat‘;

type
PVListe = ^Vliste;
Vliste = record
menge:integer;
preis:Real;
next:stuck_out_tongue:Vliste;
end;
var
f: FILE of PVliste;

Das File enthält natürlich keine Zeiger auf Daten vom Typ „VListe“, sondern Daten vom Typ „VListe“. Deshalb mußt Du das auf jeden Fall ändern zu „FILE OF Vliste“.

l: PVliste;

BEGIN
ASSIGN (f, FILENAME);
RESET (f);
while lnil do

Achtung: l wurde zu diesem Zeitpunkt noch kein Wert zugewiesen!

begin
READ (f,l);
WRITELN (‚Die Menge beträgt: ‚,l^.menge,‘‘);
WRITELN (‚Der Tagespreis beträgt: ‚,l^.preis:1:2,‘‘);
l:=l^.next;
end;
CLOSE (f);
READKEY;
END.

Check die Sache mal durch; vielleicht waren das ja schon alle Bugs.

Gruß
Martin

Hallo Marc,

Martin hat ja schon die meißten Fehler gefunden.
Bei näherem Betrachten ist mir noch aufgefallen, dass Du für die Struktur im Speicher und für das Lesen aus der Datei den selben Typ verwendest:

Vliste = record
menge:integer;
preis:Real;
next:stuck_out_tongue:Vliste;
end;

Das Record-Element

next : PVliste;

befindet sich aber garantiert nicht in der Datei.

Gruß,
Christian

PS: Verwende bitte für Source-Code den PRE-Tag, das erhöht die Lesbarkeit enorm.

Hallo,

while lnil do

hier würde ich schreiben:

while not eof(f) do

ich bin mir aber nicht sicher, ob die Funktion eof (end of file) in allen Pascal-Varianten vorkommt => ausprobieren.

Gruss, Niels

Hallo zusammen.

Erst einmal vielen Dank für Eure Hilfe.

Zu Michael:

Ich habe wie von Dir empfohlen „File of PVliste“ zu „File of Vliste“ geändert. Ich bekomme danach eine Fehlermeldung „Type mismatch“ nach READ (f,l). Liegt wohl daran, dass f nun Vliste ist und l PVliste. Das passt wohl nicht zusammen. Wie sollte ich das ändern.

Du schreibst, dass l noch kein Wert zugewiesen wurde. Ich kenne mich mit dynamischen Datenstrukturen noch nicht so ganz gut aus. Was für einen Wert soll ich denn zuweisen.

Zu Christian:

Du schreibst, dass „next : PVliste“ nicht in der Datei gespeichert ist. Ich habe das nur gemacht, weil es in Programmen, wo man die dynamischen Datenstrukturen nicht einlesen musste gut geklappt hat. Das hier ein Unterschied besteht, wusste ich nicht. Ich hab auch keine Idee, wie ich das jetzt hinkriegen könnte. Ich nehme an, dass das „next“ in jedem Fall drinbleiben muss, aber wie soll ich es dann bezeichnen.

Zu Niels:

Mir ist der Begriff EOF nur bei statischen Datenstrukturen über den Weg gelaufen und NIL bei dynamischen Datenstrukturen. Würde denn hier beides gehen?

Vielen Dank,

Marc

Hi nochmal,

Zu Michael:

ich fühle mich mal angesprochen, auch wenn ich Martin heiße :wink:

Ich habe wie von Dir empfohlen „File of PVliste“ zu „File of
Vliste“ geändert. Ich bekomme danach eine Fehlermeldung „Type
mismatch“ nach READ (f,l). Liegt wohl daran, dass f nun Vliste
ist und l PVliste. Das passt wohl nicht zusammen.

Ja, das hast Du richtig erkannt. Du brauchst noch eine Variable vom Typ „VListe“, die dann u. a. als Argument für die READ-Routine dient.

Wie sollte ich das ändern.

Allgemeiner Tip: In Pascal (und allen anderen gebräuchlichen Programmiersprachen) muß man ja allem Namen geben. Das allem ist dabei wörtlich gemeint, denn nicht nur Variablen wollen Namen haben, sondern auch Typen, Prozeduren, Funktionen, das Programm selbst, Units, kurz: wirklich alles. Bei so vielen Namen ist nun die Gefahr groß, daß früher oder später Wirrwarr entsteht (und das ist die Pest des Programmierens). Bei Prozedur- und Funktionnamen ist diesbezüglich zwar wenig zu befürchten, aber bei Variablen und Typen kann man verflucht schnell den Überblick verlieren – vielleicht hast Du das selbst schon gespürt. Weil das allen Programmierern so geht, hat sich unter Pascal eine einfache, aber wirkungsvolle Konvention eingebürgert:

Alle T ypbezeichner (mit Ausnahme der „elementaren“ Typen wie INTEGER, BOOLEAN, POINTER, STRING) beginnen stets mit einem „T“.

Hält man sich daran, kann man künftig alle Typbezeichner sofort daran erkennen, daß sie mit einem „T“ anfangen. Zusätzlich kann man sich auch noch angewöhnen, alle Zeigervariablen mit einem großen „P“ beginnen zu lassen. Das ist dann aber auch schon alles!

Deinen

record
menge:integer;
preis:Real;
next:stuck\_out\_tongue:Vliste;
end;

düftest Du dann nicht „VListe“ nennen, sondern z. B. „TListenElement“, denn es wird ja ein Typ vereinbart. Ein Zeiger auf eine Variable dieses Typs müßte vom Typ TPTListenElement sein:

TYPE
 TPTListenElement = ^TListenElement;

 TListenElement 
 = record
 menge: integer;
 preis: Real;
 next : TPTListenElement;
 end;
~
VAR
 PListenElement: TPTListenElement;
 ListenElement : TListenElement

Guck Dir jetzt mal die beiden Variablendeklarationen an: Daß die erste Variable ein Zeiger ist, springt Dir sofort ins Auge (P…), und ebenso verhält es sich mit den Typen (T… / TPT…).

Vielleicht konnte ich Dich vom Sinn dieser Konvention überzeugen, und vielleicht magst Du sie übernehmen. Ich kann Dir versichern, daß sie einem das (ohnehin hart genug seiende) Programmiererleben wirklich ungemein erleichtert:

VAR
 Button1 : TButton;
 Button2 : TButton;
 ButtonSpezial: TButton;
 Ernie, Bert : TSesamstrassenFigur;
 Sesamstrasse : TSesamstrasse;

Du schreibst, dass l noch kein Wert zugewiesen wurde. Ich
kenne mich mit dynamischen Datenstrukturen noch nicht so ganz
gut aus. Was für einen Wert soll ich denn zuweisen.

Das hat mit dynamischen Datenstrukturen nichts zu tun. Du kannst l einfach nicht abfragen, ohne daß es einen definierten Wert hat. Später bekommt l ja Werte beim Aufruf von „READ“.

Du schreibst, dass „next : PVliste“ nicht in der Datei
gespeichert ist. Ich habe das nur gemacht, weil es in
Programmen, wo man die dynamischen Datenstrukturen nicht
einlesen musste gut geklappt hat. Das hier ein Unterschied
besteht, wusste ich nicht. Ich hab auch keine Idee, wie ich
das jetzt hinkriegen könnte. Ich nehme an, dass das „next“ in
jedem Fall drinbleiben muss, aber wie soll ich es dann
bezeichnen.

TYPE
 TDateiElement 
 = record
 menge: integer;
 preis: Real;
 end;

 TPTListenElement = ^TListenElement;

 TListenElement 
 = record
 DateiElement: TDateiElement;
 PNextLE : TPTListenElement;
 end;

Um den Rest mußt Du Dich jetzt aber selber kümmern.

Mit freundlichem Gruß
Martin

Hallo,

Mir ist der Begriff EOF nur bei statischen Datenstrukturen
über den Weg gelaufen und NIL bei dynamischen Datenstrukturen.
Würde denn hier beides gehen?

in deinem Programmschnipsel liest du dein Inhalt deiner Datei sequentiell aus. Dabei stößt du irgendwann auf das Dateiende (bei einer leeren Datei bereits beim Öffnen derselben). Und das wird mit eof abgefragt.

Aber wie gesagt: das ist Delphi. Da du die Datei mit Assign zuweist, benutzt du kein Delphi (dort heisst das AssignFile). Des wegen musst du eben ausprobieren, ob das bei dir funktioniert.

Gruss, Niels

Moin Marc,

Erst einmal vielen Dank für Eure Hilfe.

Keine Ursache.

Niels und Martin haben Dir ja schon das wesentliche geschrieben.

Nur noch etwas zu EOF ( E nd O f F ile) und NIL ( N ot I n L ist):
Die Funktion EOF(f) findet sich in allen Pascal-Dialekten und prüft, ob das Dateiende von f erreicht ist. f ist eine Dateivariable, die an einen Dateinamen gebunden sein muß (-> Assign). Natürlich muß die Datei auch geöffnet sein (-> Reset).

NIL hingegen ist eine vordefinierte Konstante.
Pointer zeigen ja bekanntlich auf einen Speicherbereich im Arbeitsspeicher. Damit sie das tun, muß ihnen ein Speicherbereich zugewiesen werden (z.B. mit NEW§:wink:. Danach enthält der Pointer die Adresse des zugewiesenen Speicherbereiches.
Um nun anzuzeigen, daß kein Speicherplatz zugewiesen wurde, existiert die spezielle Konstante NIL.

hth
Christian

Hallo Marc,

deine Angaben sind recht spärlich, aber ich versuch einmal dir einen Lösungsansatz zu geben.

  1. Wie dir schon geantwortet wurde muss deine Dateivariable vom Typ VList und nicht vom Typ PVList sein.

  2. Die Überprüfung der while-Bedingung muß while eof(f) lauten.

  3. Dein Ansatz mit der Liste ist nicht korrekt. Zum besseren Verständniss. Für eine dynamische Liste benötigt man einen Zeiger auf den Anfang der Liste und einen Zeiger auf den aktuellen Eintrag der Liste oder du erzeugst eine doppelt verkettete Liste die zusätzlich zum Feld Next auch ein Feld Prev beinhaltet, da du sonst nach dem ersten Durchlauf der while-Schleife keinen Zugriff auf die vorherigen Elemente der Liste hast.

  4. Du mußt deine Liste inizialisieren in dem du für die Variable l Speicher reservierst, dies tust du mit dem Befehl New(l);. Dieser Speicher muß natürlich im späteren Verlauf wieder mit dem Befehl Dispose(l); freigegeben werden. Das Gleiche gilt natürlich auch für alle weiteren Einträge in der Liste.

  5. Wenn in der Datei nur die beiden Werte Menge und Preis stehen, warum versuchst du mit deinem Rekord dann die Werte Menge, Preis und PVList auszulesen?

  6. Ein allgemeiner Tipp, Bezeichner in Pascal dürfen beliebig lang sein wobei nur die ersten 64 Zeichen ausgewertet werden. Warum also „l“ und nicht „ListItem“? Wenn das Programm in 1 oder 2 Jahren nochmals geändert werden muss, wer hat dann noch eine Ahnung was „l“ ist? „ListItem“ hingegen sagt alles.

Ein Demo schicke ich dir per Mail.

Gruß
Mike

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Mike,

  1. Die Überprüfung der while-Bedingung muß while eof(f)
    lauten.

Nur wegen der Sicherheit: es muß while not eof(f) heißen.

bfn
Christian

Hi Christian

Nur wegen der Sicherheit: es muß while not
eof(f)
heißen.

Sorry, mein Fehler. Du hast natürlich recht.

Gruß
Mike

Vielen Dank Euch allen!
Hallo zusammen,

Vielen Dank für Eure Hilfe und dabei insbesonderem Michael, der mir eine funktionierende Datei mit reichlich Anmerkungen geschickt hat, die mir beim weiteren Lernen sehr behilflich sein werden. Nochmals Danke, dass Du Dir dafür die Zeit genommen hast.

Natürlich auch ein Danke Schön an Martin (tut mir leid, dass ich Michael geschrieben habe. Aber schon komisch, dass mir danach noch ein Michael geantwortet hat :wink:) für die Hinweise wie man sein Programm besser organisiert und natürlich auch an Niels und Christian.

Viele Grüße,

Marc Borsos