Übungsblatt 9 15 Dateien Machen Sie nun ein neues Paket mit dem Namen fileio. Da werden Sie alle Klassen von dieser Übung schreiben. Machen Sie da eine neue Klasse: Test.java. Da sollen Sie danach die Paketen java.util.*; und java.io.*; importieren und die folgende Methode schreiben: 1: public static void readLines(String fileName) throws Throwable { 2: File file = new File(fileName); 3: Scanner fin = new Scanner(file); 4: while(fin.hasNextLine()) { 5: System.out.println(fin.nextLine()); 6: } 7: fin.close(); 8: } Diese Methode liest den Inhalt der genannten Datei Zeile für Zeile, solange es weiteren Zeilen in der Datei gibt. Jede Zeile wird gerade ausgedruckt, nachdem sie gelesen wurde. In der Zeile 2 des Programmstücks wird eine Repräsentation der Datei mit gegebenem Dateinamen erzeugt. Sehen Sie java.sun.com19 für mehr Informationen über dem Typ File. In der Zeile 3 wird ein Scanner erzeugt, der zum Lesen dieser Datei benutzt wird. Die Methode hasNextLine() (Zeile 4) liefert true zurück, wenn man eine weitere Zeile aus der Datei lesen kann, sonst false. Mit der Methode nextLine() (Zeile 5) bekommt man die nächste noch nicht gelesene Zeile als ein String und ohne Zeilenumbruch. Die Anweisung „throws Throwable“ (Zeile 1) bedeutet, dass unser Code Ausnahmen werfen könnte, welches normalerweise mit einer try-catch Anweisung behandeln wird. Da Ausnahmen aber kein Thema dieser Übung sind, wird diese Anweisung bei jeder Methode stehen, die eine Datei mit Scanner liest, oder solche eine Methode ruft. Nun können Sie eine Datei test.txt in der Basisdatenverzeichnis des Projekts machen (Rechtklick auf den Namen des Projekts in Package Explorer; New -> File auswählen; den Namen eingeben und den Finish-Knopf klicken). Schreiben Sie ein Paar Zeilen in dieser Datei und speichern Sie sie. Nun kehren Sie zurück zur Ihren Klasse und schreiben Sie die Methode main: public static void main(String[] args) throws Throwable { readLines("test.txt"); } Nachdem Sie die Klasse rennen, sollen Sie den Inhalt der Datei auf stdout bekommen. 19 http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html 26 15.1 Input filtrieren 5a: 5b: 5c: 5d: String line = fin.nextLine(); if(line.length() <= 80) { System.out.println(line); } Hier speichert man Zuerst die gelesene Zeile in einem String line und danach druckt man diesen String aus, nur wenn seine Länge kleiner oder gleich 80 ist. Mit der Bedingung in der Zeile 5b kann man also entscheiden welche Zeilen enier Datei zu verarbeien sind. Zum Beispiel können wir auch unsere neue Methode kopieren, die Kopie als readLinesWithAWord nennen und die Zeile 5b so ändern: 5b: if(line.contains("test")) { Dann werden nur die Zeilen ausgedruckt, die einen Teilstring "test" enthalten. 15.2 Andere Typen Ausser den Zeilen, können wir mit der Klasse Scanner auch andere Tokens wie ein Wort (next()), ein Integer (nextInt()) oder ein Double (nextDouble()) lesen. Wenn wir zum Beispiel unsere letzte Methode kopieren, die Kopie sumNumbers nennen, und sie ab Zeile 4 so schreiben: 4: 5: 6: 7: 8: 9: int sum = 0; while(fin.hasNextInt()) { sum += fin.nextInt(); } fin.close(); System.out.println(sum); Dann wird diese Methode die ganze Zahlen aus einer Datei lesen und ihre Summe auf stdout ausdrucken. Es kann mehrere Zahlen in einer Zeile geben aber die einzige erlaubte Separatoren sind Zwischenraumzeichen (auf engl. whitespaces). Wenn Scanner nach der zuletzt gelesener Zahl keine Zahl mahr findet, wird hasNextInt schon false zurückliefern. Wenn man die Zahlen als Objekte speichern will, muss man statt primitiven Datentypen int und double die Klassen Integer und Dobule verwenden. Es folgt ein Beispiel, wo man die Zahlen aus einer Datei sortiert druckt: public static void sortNumbers(String fileName) throws Throwable { File file = new File(fileName); // Alles initialisieren Scanner fin = new Scanner(file); ArrayList<Integer> numbers = new ArrayList<Integer>(); while(fin.hasNextInt()) { // Die Zahlen lesen und speichern numbers.add(fin.nextInt()); } fin.close(); // Die Datei so früh wie möglich schlieÿen Collections.sort(numbers); // Sortieren for(Integer number : numbers) { // Oder "int number". Hier ist es schon egal. System.out.println(number); } } 27 Autor: M. A. Nun möchten wir unsere Aufgabe erweitern. Machen Sie eine Kopie dieser Methode, und nennen Sie sie readLines80. Modifizieren Sie danach in dieser Methode den Körper von while: 15.3 Die Klasse String Damit Sie die Eingabe von einer Datei bequem verarbeiten können, sollen Sie die Klasse String20 beherrschen. Hier werden einige Funktionalitäten angegeben. Methode Beschreibung / Ergebnis char charAt(int pos) Zeichen auf der Position pos. int compareTo(String str) Lexikographischer Vergleich mit str. Wie strcmp in C. boolean contains(CharSequence s) Liefert true, wenn der String s enthalten ist. boolean equals(Object obj) Vergleich zum anderen Objekt obj (kann auch String sein). int indexOf(String str) Index des ersten Vorvalls von str. Wenn es keinen gibt, wird -1 zurückgeliefert. int indexOf(String str, int pos) Index des ersten Vorvalls von str ab Position pos. int length() Länge des Strings. String replace(String from, Erzeugt eine Kopie des Strings, bei der alle "from" TeilString to) strings mit "to" ersetzt sind. String substring(int begin) Erzeugt eine Kopie des Teilstrings, der auf der Position begin beginnt. String substring(int begin, int Erzeugt eine Kopie des Teilstrings, der unter Indexe end) [begin, end) liegt. String toLowerCase() Erzeugt eine Kopie des Strings in Kleinschrift. String toUpperCase() Erzeugt eine Kopie des Strings in Großschrift. String trim() Erzeugt eine Kopie des Strings ohne Zwischenraumzeichen am Beginn und am Ende. Oft werden Sie ein Zusammenspiel von diesen und anderen Methoden benutzen, um das gewünschte Ergebnis zu kriegen. Wenn Sie zum Beispiel brauchen, den ersten Buchstaben von String str groß und alle andere Buchstaben klein zu machen, wird es so aussehen: str = str.substring(0,1).toUpperCase() + str.substring(1).toLowerCase(); 15.4 Schreiben zu einer Datei Bevor wir mit den Aufgaben beginnen, wird es noch kurz angegeben, wie kann man zu einer Datei schreiben. Beobachten Sie den folgenden Code: public static void testWrite() throws Throwable { FileWriter fstream = new FileWriter("out.txt"); // FileWriter aus java.io.*; BufferedWriter out = new BufferedWriter(fstream); // BufferedWriter aus java.io.*; out.write("Hallo"); out.close(); } So werden Sie eine neue Datei out.txt mit dem Inhalt "Hallo" machen (oder eine alte überschreiben). Machen Sie eine Aktualisierung (refresh, F5) des Projektbaums, um diese Datei zu sehen. Wenn Sie den vorherigen Inhalt der Datei nicht überschreiben wollen, geben Sie beim Konstruktor von FileWriter als zweites Argument true ein. FileWriter fstream = new FileWriter("out.txt", true); 20 http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html 28 15.5 Aufgaben Lösen Sie nun die folgende Aufgaben. Jede Aufgabe soll in einer eigenen Klasse geschrieben werden. Jede Klasse soll die main-Methode zur Ausführung und eine statische loesung-Methode zur Lösung haben. Die Namen der Dateien müssen Argumenten der Methode loesung sein. Man darf auch Hilfsmethoden schreiben. 1. Klasse Mittelwert: Lesen Sie alle ganze Zahlen aus einer Datei und berechnen Sie ihres arithmetische Mittel als eine reelle Zahl. Behandeln Side den Fall wenn es keine Zahl in der Datei gibt. 2. Klasse DMatrix: Lesen Sie eine Quadratmatrix der reellen Zahlen aus einer Datei. In der Datei wird zuerst die Dimension der Matrix und dann die Matrix selbst angegeben. Die Matrix soll man als double[][] mat speichern. Drucken Sie diese Matrix auf stdout und behandeln Sie ein Paar möglichen Fehler: (1) Wenn kein Integer am Anfang steht. (2) Wenn es nicht genug Zahlen für die ganze Matrix gibt, füllen sie den Rest der Matrix mit Nullen aus. 3. Klasse PrintBackwards: Lesen Sie den Inhalt einer Datei Zeile für Zeile. Dabei ist Ihre Aufgabe jede Zeile Rückwärts auf stdout auszudrucken. Zum Beispiel, wenn eine Eingabezeile "abcdef" wäre, würde ihre Ausgabezeile "fedcba" lauten. Benutzen Sie dabei von der Klasse String nur Methoden length() und charAt(). 4. Klasse MergeFiles: Es sind die Namen von zwei Dateien gegeben. Sie sollen eine dritte Datei erzeugen, in der Sie zuerst die erste und danach die zweite Datei abschreiben. 5. Klasse MixFiles: Es sind die Namen von zwei Dateien gegeben. Sie sollen eine dritte Datei erzeugen, in der Sie wechselweise eine Zeile von der ersten Datei und eine Zeile von der zweiten Datei abschreiben. Wenn eine Datei zu Ende kommt, schreiben Sie ihre Zeilen einfach nicht weiter. 6. Klasse ExtractData: Es ist eine XML Datei gegeben, wo meist der Zeilen Format: <bl title="Mathematik" /> haben. Ihre Aufgabe ist nur die Zeilen mit dem Teilstring "title=" zu verarbeiten. Aus jeder solchen Zeile sollen Sie den Teilstring zwischen " und " ausziehen (dieser Teilstring bei der obigen Zeile würde Mathematik lauten). Diese Teilstrings sollen Sie in eine andere Datei schrieben, wobei jeder Teilstring in seiner eigenen Zeile ist. Es dürfen keine leere Zeilen zwischen den vollen erscheinen. 29