Daten aus einer Datei lesen

Bis jetzt hatte wir alle Daten die benötigt werden, entweder direkt im Programm abgelegt, oder zur Laufzeit des Programms eingegeben. Das hatte zur Folge das alle Daten die wir eingegeben hatten, nach der Ausführung des Programms wieder weg wahren. Um Daten dauerhaft zu speichern schreibt man diese normalerweise in eine Datei. In JAVA ist dieser, in anderen Programmiersprachen recht einfache Vorgang, doch mit einigen Komplikationen verbunden.

Fangen wir mit dem lesen von Informationen aus einer Datei an.
In unserem Programm Kaffeetassen sind die Daten zu den Tassen bisher in einem Array gespeichert. Dieses Array wollen wir nun in eine Datei verlegen. Dazu erzeugen wir im Ordner C:\justfun eine Textdatei mit dem Namen Tassen.txt. Die Datei hat folgenden Inhalt:

                         Weiß,10,8,Ja,KIKISWEB
                         Schwarz,10,8,Ja,JAVA
                         Blau,10,8,Ja,SCHALKE
                         Rot,10,8,Ja,BAYERN
                         Gelb,10,8,Nein,BVB

Also im Prinzip das, was in unserem Array steht:


                 [...]
                         Tassen[0] = new Tasse("Weiß", 10, 8, "Ja", "KIKISWEB");
                         Tassen[1] = new Tasse("Schwarz", 10, 8, "Ja", "JAVA");
                         Tassen[2] = new Tasse("Blau", 10, 8, "Ja", "SCHALKE");
                         Tassen[3] = new Tasse("Rot", 10, 8, "Ja", "BAYERN");
                         Tassen[4] = new Tasse("Gelb", 10, 8, "Nein", "BVB");
                 [...]

Wie bekommt man nun diese Informationen aus der Datei in das Programm?
Bei der Eingabe von Daten mit der Tastatur hatten wir BufferedReader kennengelernt, mit dessen Hilfe wir die Eingaben in unserem Programm verwerten konnten. Im Prinzip machen wir beim Einlesen der Datei nichts anderes, nur das wir als Quelle eine Datei benutzen.
Um eine Datei lesen zu können, müssen wir zuerst ein Objekt erzeugen, das unsere Datei darstellt. In JAVA macht das die Klasse FileReader. Mit FileReader Datei = new FileReader("C:\\justfun\\Tasse.txt"); erzeugen wir dieses Objekt.
Den Pfad für die Datei "C:\justfun\Tasse.txt" müssen wir als "C:\\justfun\\Tasse.txt" angeben, da \ ein Sonderzeichen ist, das besonders behandelt wird. Sonderzeichen müssen in einem String gekennzeichnet werden, damit das Programm sie nicht als Anweisung interpretiert. Deshalb wird vor einem Sonderzeichen ein \ in den String eingefügt, bei unserem Pfad also \\.
Alternativ kann man für den Pfad aber auch / benutzen.(Funktioniert auch unter Windows)
Als nächstes wird unser BufferedReader für diese Datei erzeugt: BufferedReader in = new BufferedReader(Datei);.
Damit haben wir die Voraussetzungen geschaffen, um eine Datei einlesen zu können.

Jetzt können wir die Datei Zeilenweise auslesen:


                         [...]
                         zeile = in.readLine();

                         while(zeile != null){
                                 System.out.println(zeile);
                                 zeile = in.readLine();
                         }
                         [...]

Wir lesen die erste Zeile aus unserer Datei in die Variable zeile. Dann folgt eine bedingte Schleife mit While, die prüft, ob die eingelesene Zeile leer ist. null ist die Bezeichnung für "diese Variable hat keinen Inhalt". Hat die Zeile einen Inhalt, wird dieser ausgegeben und die nächste Zeile wird eingelesen. Das erfolgt so lange, bis eine Zeile keinen Inhalt mehr hat, also das Dateiende erreicht wurde.
Zum Schluß muss die geöffnete Datei wieder geschlossen werden, dies geschieht mit in.close();.

"Am Stück" sieht das dann so aus:


                         import java.io.*;

                         class Datei {

                                 public static void main(String[] args) throws IOException {

                                        .//Das Datei-Objekt erzeugen
                                         FileReader Datei = new FileReader("C:\\justfun\\Tasse.txt");

                                        .//Die Datei öffnen und einen Input Stream für die Datei erzeugen
                                         BufferedReader in = new BufferedReader(Datei);

                                        .//Die erste Zeile lesen
                                         String zeile = in.readLine();

                                        .//Solange Daten vorhanden sind
                                         while( zeile != null ){
                                                 System.out.println(zeile);
                                                 zeile = in.readLine();
                                         }

                                        .//Datei schließen
                                         in.close();
                                 }
                         }
                         [...]

Wir können also eine Datei Zeilenweise lesen. Schön! Wie bekommen wir jetzt die Daten in unser Programm Kaffetassen?
Im Moment haben wir in jeder Zeile eine Liste der Eigenschaften unserer Tassen, die durch Komma getrennt sind, auf englisch, comma seperated values (csv). Betrachten wir also die Zeilen einzeln.
Es gibt in java.util eine Klasse namens StringTokenizer, mit dieser Klasse ist es möglich, Informationen, die durch ein Trennzeichen, bei uns das Komma, voneinander unterschieden werden können, einzeln zu bearbeiten.

Gehen wir ans Werk und ändern unser Programm Kaffeetassen. Erst einmal entfernen wir das Array aus dem Code, wir wollen die Daten ja aus der Datei lesen. Dann öffnen wir die Datei und erzeugen den Input Stream. Nun lesen wir die Datei Zeilenweise ein und weisen den einzelnen Variablen die passenden Elemente zu. Zum Schluß werden, wie immer, die Tassen ausgegeben.



         import java.io.*;
         import java.util.*;

         class Kaffeetassen {

                 /* Programm Kaffeetassen von Heiko Schmuck, im März 2011
                  * Das Programm beschreibt Kaffetassen und gibt die Beschreibung
                  * auf dem Bildschirm aus.
                  * In der neunten Version werden die Tassen aus einer Datei gelesen.
                  */

                 // Eine Variable, die mit den Daten für die jeweilige Tasse gefüllt wird.
                 Tasse dataTasse;

                 // In diese Variablen kommen die Eigenschaften der jeweiligen Tasse.
                 String strFarbe, strHenkel, strAufdruck;
                 int intHoehe, intDurchmesser;

                 // Kaffeetassen haben die Eigenschaften: Farbe, Höhe, Durchmesser, Henkel, Aufdruck
                 Kaffeetassen () {

                         //Das Datei-Objekt erzeugen
                         FileReader Datei = new FileReader("C:\\justfun\\Tasse.txt");

                         //Die Datei öffnen und einen Input Stream für die Datei erzeugen
                         BufferedReader in = new BufferedReader(Datei);

                         // Erst einmal eine Überschrift
                         System.out.println("Meine Kaffeetassen:");

                         //Die Zeilen in einer FOR-Schleife einlesen
                         for(String zeile = in.readLine(); zeile != null; zeile = in.readLine()){
                                StringTokenizer data = new StringTokenizer(zeile, ",");

                                 //Die Eigenschaften in Variablen ablegen
                                 strFarbe = data.nextToken();
                                 intHoehe = new Integer(data.nextToken());
                                 intDurchmesser = new Integer(data.nextToken());
                                 strHenkel = data.nextToken();
                                 strAufdruck = data.nextToken();

                                 //Das Objekt Tasse erzeugen und ausgeben.
                                 dataTasse = new Tasse(strFarbe, intHoehe, intDurchmesser, strHenkel, strAufdruck);
                                 dataTasse.ausgeben();
                         }

                         //Datei schließen
                         in.close();


                 // und den Konstruktor Kaffetassen wieder schließen.
                 }

                 [...]

Ein bischen viel auf einmal? OK, gehen wir das ganze mal Zeilenweise durch.
Wie immer werden zuerst einige Variablen erzeugt. Soweit ist alles klar. Als nächstes wird mit FileReader Datei = new FileReader("C:\\justfun\\Tasse.txt"); ein Dateiobjekt für die Datei Tasse.txt geöffnet. Für diese Dateiobjekt erzeugen wir mit BufferedReader in = new BufferedReader(Datei); einen Kanal, der die Daten einlesen kann. Dann geben wir die Überschrift aus und lesen in der FOR-Schleife for(String zeile = in.readLine(); zeile != null; zeile = in.readLine()) die einzelnen Zeilen. Jede Zeile wird einem StringTokenizer übergeben. Dieser teilt die Zeilen in einzelne, durch "," getrennte, Strings. Mit data.nextToken();, wird jeweils der nächste Teil der Zeile an eine der Variablen übergeben. Diese Variablen werden danach in ein Tasse-Objekt geschrieben und die Tasse wird, wie immer, ausgegeben.

Jetzt könnte man fragen, "Warum kann man nicht sofort die ganze Zeile als Tasse einlesen?". Die Methode .readLine() ließt die Zeile in der Datei als ganzes, das heißt es wird nur ein String erzeugt, der die Zeile enthält. Würden wir diesen String dem Objekt Tasse übergeben, würde zurecht ein Fehler ausgegeben, dass vier der fünf Übergabeparameter nicht gefüllt sind. Zudem sind zwei der Parameter vom Typ int, was zu weiteren fehlern führen würde.

Das Programm "am Stück" gibt es hier.

Weiter geht es mit dem Schreiben in eine Datei.

Mit JAVA programmieren

Als Entwicklungsumgebung benutze ich NetBeans 7.