1 Ein- und Ausgabe von Daten

1.1 Ein- und Ausgabe von Textzeilen
1.2 Ein- und Ausgabe von numerischen Werten
1.3 Probleme bei der Daten-Eingabe in Programmen
Zum Inhaltsverzeichnis Zum nächsten Kapitel


1.1 Ein- und Ausgabe von Textzeilen

Für die Eingabe von Texten werden üblicherweise spezielle Text-Felder verwendet. Zur Laufzeit fokussiert der Benutzer das gewünschte Feld (z.B. indem er mit der Maus draufklickt), und dann kann er seinen Text über die Tastatur eingeben.
Delphi stellt uns zu diesem Zwecke eine entsprechende "TEdit"-Komponente zur Verfügung, welche zur Laufzeit unseres Programms die Eingabe von (kurzen) Texten erlaubt. Der Zugriff auf die eingegebene Zeichenkette ist über die Eigenschaft "Text" der TEdit-Komponente möglich, welche vom Datentyp "String" ist. Der folgende Quelltext liest die TEdit-Komponente "Edit1" aus und kopiert den enthaltenen Text in die String-Variable s:
     var s: String;
     begin
     s := Edit1.Text;
     {......}
     end;
Die Programmzeile mit dem ":=" ist eine Zuweisung, mit der der Inhalt der rechts vom Zuweisungszeichen stehenden Variablen in die links vom Zuweisungszeichen stehende Variable kopiert wird. Zuweisungen haben also stets die folgende Struktur:
Java stellt uns zu diesem Zwecke eine entsprechende "TextField"-Komponente zur Verfügung, welche zur Laufzeit unseres Programms die Eingabe von (kurzen) Texten erlaubt. Der Zugriff auf die eingegebene Zeichenkette ist über die Methode "getText()" der "TextField"-Komponente möglich. Der folgende Quelltext liest die TEdit-Komponente "textField1" aus und kopiert den enthaltenen Text in die String-Variable s:
     String s;

     s = textField1.getText();
     {......}

Die Programmzeile mit dem "=" ist eine Zuweisung, mit der der Inhalt der rechts vom Zuweisungszeichen stehenden Variablen in die links vom Zuweisungszeichen stehende Variable kopiert wird. Zuweisungen haben also stets die folgende Struktur:
Dabei müssen "Quelle" und "Ziel" zwei zueinander kompatible Variablen sein. Beachten Sie, dass bei einer Zuweisung stets zuerst das Ziel angegeben wird; die Datenquelle folgt dann erst hinter dem Zuweisungszeichen! (Und als kurzer Merksatz: "Ziel zuerst, Daten danach!")

Ein solches Text-Feld kann genau so gut auch zur Ausgabe von Texten verwendet werden. Der folgende Quelltextausschnitt kopiert den Inhalt eines ersten Text-Feldes in ein zweites:
In Delphi ist dazu die Zuweisung im vorstehenden Quelltextfragment einfach umzudrehen:
     var s: String;
     begin
     s := Edit1.Text;
     Edit2.Text := s;
     end;
    
In Java benutzt man die Methode "setText()" vom textField2, um den in s gespeicherten Text in das textField2 zu schreiben. Dazu muss s als Argument in der Klammer an die Methode übergeben werden:
     String s;
     s = textField1.getText();
     textField2.setText(s);
    
Es besteht sogar die Möglichkeit, dem Benutzer zur Laufzeit die Eingabe in ein Ausgabe-Textfeld ganz zu untersagen: im vorstehenden Beispiel könnte man das für die Ausgabe gedachte Text-Feld für Benutzer-Eingaben komplett sperren, indem man (im Objekt-Inspektor) die entsprechende Eigenschaft ändert:
In Delphi ist dazu die Eigenschaft "Edit2.ReadOnly" auf den Wert "True" zu setzen. In Java ist dazu die Eigenschaft "textField2.Editable" auf den Wert "false" zu setzen
Programminterne Zuweisungen funktionieren jedoch nach wie vor - die genannten Eigenschaften der Text-Felder beziehen sich nur auf die Benutzeroberfläche!



1.2 Ein- und Ausgabe von numerischen Werten

Bei der Eingabe von Zahlenwerten mit Hilfe von Edit-Komponenten stolpern wir über das Problem des falschen Variablentyps: im Editfeld steht eine Zeichenkette, aber wir wollen einen numerischen Wert, d.h. eine Zahl haben. Hier wird's dann auch gleich etwas schwieriger, weil alle Programmiersprachen bei den Zahlen verschiedene Typen unterscheiden: zur Speicherung und Bearbeitung von ganzen Zahlen wird meist der Delphi-Variablentyp "Integer" bzw. der (identische!) Java-Variablentyp "int" benutzt; für die Fließkommazahlen (Dezimalzahlen) gibt es gleich mehrere Typen, die sich durch die Genauigkeit unterscheiden, mit der der Zahlenwert gespeichert wird. Während eine Fließkommazahl auch einen ganzzahligen Wert haben kann, ist der umgekehrte Fall nicht möglich: eine ganze Zahl kann keinen (echten) Fließkommawert speichern. Man muss sich also immer vorher überlegen, was man alles mit einer Zahl vorhat, und dann den passenden Typ dafür wählen. Die folgende Tabelle listet die Eigenschaften der 3 genannten numerischen Variablentypen auf:

Delphi-Typ Java-Typ Größe Zahlbereich Genauigkeit
Integer int 4 Byte Ganze Zahlen aus dem Bereich
[-2147483648, 2147483647]
(exakt)
Single float 4 Byte Fließkommazahlen mit Betrag aus
[1.5 x 10-45, 3.4 x 1038]
7-8 geltende Stellen
Double double 8 Byte Fließkommazahlen mit Betrag aus
[5.0 x 10-324, 1.7 x 10308
15-16 geltende Stellen

Zur programminternen Verarbeitung von Zahlenwerten müssen wir zunächst entsprechende Variablen zur Verfügung stellen. Man kann sich solche Variablen als Schachteln vorstellen, auf denen der Name der Variablen steht und in denen der aktuelle Wert der Variablen liegt. Die Erzeugung einer solchen Variablen geschieht durch eine Variablen-Deklaration. Wenn wir z.B. eine ganze Zahl in einer Integer-Variablen namens "n" ablegen wollen, dann müssen wir zunächst die folgende Deklaration in den Quelltext schreiben:
     var n : Integer; 
In Delphi beginnt eine Variablen-Deklaration also stets mit dem Schlüsselwort "var"; dann folgt der Variablenname, dann ein Doppelpunkt und schließlich der Variablentyp. Die Deklaration wird mit einem Strichpunkt abgeschlossen. Variablennamen können auch länger sein, aber sie dürfen keine Sonderzeichen (wie Umlaute, scharfes S, Akzente usw.) enthalten. Man kann in einer Deklaration auch mehrere Variablen gleichzeitig deklarieren. Wenn wir z.B. in einem Programm zwei Integer-Variablen "n1" und "n2" brauchen und eine Double-Variable namens "dezimalzahl", dann erreichen wir dies mit der folgenden Deklaration:
     var n1, n2      : Integer;
         dezimalzahl : Double; 
     int n; 
In Java beginnt eine Variablendeklaration also stets mit der Nennung des gewünschten Variablentyps; dann folgt der Variablenname. Die Deklaration wird mit einem Strichpunkt abgeschlossen. Variablennamen können auch länger sein, aber sie dürfen keine Sonderzeichen (wie Umlaute, scharfes S, Akzente usw.) enthalten. Wenn Sie mehrere Variablen vom gleichen Typ simultan deklararieren wollen, dann können Sie nach dem Typnamen auch mehrere Variablennamen aufführen, die durch Kommata getrennt sind:
     int n1, n2, n3, summe; 
Alternativ kann man einer Variablen aber auch gleich bei der Deklaration einen bestimmten Wert zuweisen. Dies geht folgendermaßen:
     int n1 =  5;
     int n2 = -8;
     double dezi = 3.14; 
Wie erhält nun eine solche numerische Variable zur Laufzeit unseres Programms den jeweils gewünschten Wert? Nehmen wir mal an, dass der Benutzer unseres Programms dazu in ein Text-Feld eine Zeichenkette eingibt, die den Zahlenwert darstellt. Aber Vorsicht! Auch wenn im Text-Feld der Text "123" steht, ist dies ein String, und den können wir nicht in eine Integer- oder Double-Variable schreiben: vor der Zuweisung an eine numerische Variable muss diese Zeichenkette erst noch in einen entsprechenden numerischen Wert konvertiert werden! Glücklicherweise gibt es für diese nicht ganz einfache Aufgabe schon fertige Funktionen:
In Delphi interpretiert "StrToInt()" das übergebene String-Argument als ganze Zahl und gibt deren Wert zurück, "StrToFloat()" interpretiert den übergebenen String als Fließkommazahl. Das folgende Programmfragment zeigt, wie aus dem TEdit-Feld Edit1 eine Integerzahl und aus dem TEdit-Feld Edit2 eine Fließkommazahl ausgelesen werden können:
     var n : Integer;
         res : Double;
     begin
     n   := StrToInt(Edit1.Text);
     res := StrToFloat(Edit2.Text);
     {.......}
     end;
In Java interpretiert die Methode "parseInt()" der Klasse "Integer" das übergebene String-Argument als ganze Zahl und gibt deren Wert zurück, "Double.parseDouble()" interpretiert den übergebenen String als Fließkommazahl und gibt deren Wert zurück. Das folgende Programmfragment zeigt, wie aus dem Text-Feld textField1 eine Integerzahl und aus dem Text-Feld textField2 eine Fließkommazahl ausgelesen werden kann:
     int n;
     double res;

     n   = Integer.parseInt(textField1.getText());
     res = Double.parseDouble(textField2.getText());

Bei der Ausgabe von numerischen Werten haben wir das umgekehrte Problem: wenn unser Programm das Ergebnis einer numerischen Berechnung in einer Fließkomma-Variablen "res" abgelegt hat, wie bekommen wir dann die in "res" gespeicherte Zahl in ein Text-Feld?
Die naheliegende Zuweisung
     Edit3.Text := res;
wird vom Delphi-Compiler mit einer Fehlermeldung "Inkompatible Typen 'TCaption' und 'Double'" zurückgewiesen. Wir müssen also erst aus dem in "res" abgelegten Wert einen String basteln, den wir dann in das Feld Edit3.Text kopieren können. Glücklicherweise gibt es auch dafür in Delphi schon eine fertige Funktion, nämlich "FloatToStr()". Die korrekte Zuweisung für die Ausgabe der in res gespeicherten Zahl lautet also:
     Edit3.Text := FloatToStr(res);
Es wird Sie nun nicht mehr wundern, dass es für die Ausgabe von Integer-Werten auch eine entsprechende Funktion namens "IntToStr()" gibt.
Die naheliegende Zuweisung
     textField3.setText(res);
wird vom Java-Compiler mit der wenig deutlichen Fehlermeldung "cannot find symbol" zurückgewiesen, wobei zu erkennen ist, dass das Symbol "setText()" gemeint ist. Dies soll bedeuten, dass diese Methode nicht mit einer "double"-Variablen als Argument aufgerufen werden kann! Wir müssen also erst aus dem in "res" abgelegten Wert einen String basteln, den wir dann an "setText()" übergeben können, um ihn in das Feld "textField3" kopieren zu lassen. Glücklicherweise gibt es auch dafür in Java schon eine fertige Funktion, nämlich "Double.toString()". Die korrekte Zuweisung für die Ausgabe der in "res" gespeicherten Zahl lautet also:
     textField3.setText(Double.toString(res));
Es wird Sie nun nicht mehr wundern, dass es für Konvertierung von Integer-Werten in Strings auch eine entsprechende Funktion namens "Integer.toString()" gibt.


Nun sollten Sie fit sein für die folgenden Aufgaben:

  1. Das Taxi-Problem:

    Ein Programm soll eine Taxi-Rechnung erstellen: jeder gefahrene Kilometer kostet 90 Cent, jeder dabei transportierte Koffer 1,50 €. Es soll die Fahrstrecke und die Kofferzahl eingegeben werden, und das Programm soll den Rechnungsbetrag ausgeben.

    Achten Sie auf eine übersichtliche und benutzerfreundliche Darstellung des Fensters. Kommentare und Regieanweisungen kommen in TLabel-Komponenten.
    Lösungsvorschlag: [Delphi] [Java]


  2. Das Heizöl-Problem:

    a) Ein Programm soll berechnen, wie hoch die Rechnung bei einer Lieferung von Heizöl ist. Ein Vorschlag für den Ölpreis: 65 Cent/Liter (ohne Mehrwertsteuer). Als Eingaben sollen der Anfangsfüllstand und der Endfüllstand des Tanklastzugs entgegen genommen werden. Ausgegeben werden sollen die gelieferte Ölmenge in Litern, der Netto-Wert in Euro, die Mehrwersteuer und der Rechnungsbetrag (incl. MWSt.). Achten Sie dabei darauf, dass die Geldbeträge alle mit genau zwei Nachkommastellen ausgegeben werden. In Delphi können Sie dazu die Funktion "RoundTo()" benutzen, in Java bietet sich "Math.rint()" an. Näheres finden Sie in der jeweiligen Online-Hilfe.
    Lösungsvorschlag: [Delphi] [Java]
    b) Zusatzaufgabe für unterforderte Profis:
    Sichern Sie zunächst den aktuellen Zustand Ihres Projekts. Kopieren Sie dann das gesamte Projekt in ein neues Verzeichnis. Können Sie das Programm nun so verändern, dass es ohne Einbuße an Funktionalität auch ohne eine Schaltfläche zur Auslösung der Berechnung auskommt?
    Lösungsvorschlag: [Java]


  3. Das TR-Problem:

    a) Schreiben Sie einen Taschenrechner, der die 4 Grundrechenarten für ganze Zahlen beherrscht: zwei Eingabefelder, 4 Schaltknöpfe und ein Ausgabefeld sollten genügen.

    Das mögliche Problem der "Division durch Null" können Sie in der ersten Ausbaustufe zunächst unbeachtet lassen. Um dieses Problem lösen zu können, müssten Sie eine Entscheidung programmieren. Wie man das macht, wird eigentlich erst im nächsten Kapitel behandelt, aber vielleicht wollen Sie ja schon mal selbst einen Blick in die Zukunft werfen....
    Lösungsvorschlag: [Delphi] [Java]

    b) Zusatzaufgabe für unterforderte Profis:
    Sichern Sie nun zunächst den aktuellen Zustand und kopieren Sie das gesamte Projekt in ein neues Verzeichnis. Können Sie das Programm so umbauen, dass es (wie ein Taschenrechner) mit einem einzigen Editfeld für Ein- und Ausgaben auskommt?

    Jetzt müssen Sie ihm ein Gedächtnis für die Details der Rechnung einpflanzen. Dazu müssen Sie 2 "private" Variablen deklarieren, die im gesamten Formular verfügbar sind: eine "Char"-Variable namens "rechenart" und eine "Double"-Variable namens "memory". In diesen Variablen können Sie dann die zuletzt angeklickte Rechenart und das Ergebnis des letzten Rechenschrittes abspeichern.
    Lösungsvorschlag: [Delphi]




1.3 Probleme bei der Daten-Eingabe in Programmen

Zwar wissen Sie jetzt schon, wie man in ein Programm Zahlen eingeben kann, aber Sie sind dabei auf einen klugen und konstruktiven Benutzer angewiesen. Was passiert jedoch, wenn da ein Chaot vor dem Computer sitzt, der bei der Eingabe einer ganzen Zahl statt "345" halt mal eben "3r5" eintippt? Dann erhalten wir eine Laufzeit-Fehlermeldung, weil die aufgerufene Konvertierungsmethode diese Zeichenkette nicht als Integer-Wert interpretieren kann!

Es ist geradezu ein definierendes Kennzeichen einer "guten" (d.h. stabilen) Software, dass sie gegen solche Eingabe-Fehler abgesichert ist. Im vorliegenden Fall der Integer-Eingabe wäre es z.B. wünschenswert, dass das Eingabe-Text-Feld überhaupt nur solche Zeichen annehmen würde, die Ziffern darstellen. Man sollte also falsche oder unpassende Daten schon gleich an der Programmoberfläche abfangen und zurückweisen, und erst dann mit der Verarbeitung der Daten anfangen, wenn gesichert ist, dass diese sinnvoll sind.

So leicht man sich auch auf dieses Ziel einigen kann, so schwer ist es in der Praxis zu erreichen. Und gerade für uns Anfänger ist diese Aufgabe viel zu schwierig. Wir begnügen uns also zunächst damit, dass wir hier ein Problem erkannt haben. Und wir hoffen, dass wir bald genug gelernt haben werden, um eine Lösung dafür programmieren zu können.



Zum Inhaltsverzeichnis Zum nächsten Kapitel