Arbeiten mit Dateien

Werbung
Kurs OPR
Objektorientierte Programmierung
Lektion: 011-Arbeiten mit Dateien
Zürcher Hochschule für Angewandte Wissenschaften
Mitglied der Fachhochschule Zürich
Version 1.4
Inhaltsverzeichnis 1 Arbeiten mit Dateien
3 1.1 Ziele
3 1.2 Einführung
3 1.3 Zugriff auf Dateien
3 1.4 Sequentieller Filezugriff: Streams
4 1.4.1 Direkter (byte-weiser) sequentieller Zugriff auf eine Datei
4 1.4.2 Gepufferter (blockweiser) Zugriff auf Datei
5 1.5 Java IO-Klassen
6 1.5.1 Erklärungen zu den einzelnen Klassen für das Lesen / Schreiben von Daten:
7 1.5.2 Lesen und Schreiben von Textdateien
8 1.6 Ausgabe auf ein File
10 1.7 Einlesen von einem File
13 1.8 File durchsuchen
15 1.9 Filenamen aussuchen
18 1.10 Umgang mit Dateien
20 1.11 Konsolen-IO
20 1.12 System-Klasse
21 1.13 Konsolenbasierte Anwendungen
22 1.14 Lesen von anderen Sites
24 1.15 Persistente Objekte
26 1.16 Professionelles Programmieren
29 Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
2
1 Arbeiten mit Dateien
1.1
•
•
•
•
•
1.2
•
•
•
1.3
•
•
Ziele
Sie können einem Laien erklären, was eine Datei ist.
Sie unterscheiden zwei Zugriffsarten auf eine Datei.
Sie können das prinzipielle Vorgehen bei einem File-Zugriff in Java erläutern.
Sie kennen mindestens eine Möglichkeit, Textdateien zu lesen und zu schreiben.
Sie können mit Files und Directories in Ihren Programmen umgehen.
Einführung
Daten im Arbeitsspeicher (RAM) gehen beim Ausschalten des Computers verloren.
Es braucht deshalb eine Möglichkeit, um diese Daten permanent zu speichern.
Æ Zusammengehörige Daten werden zu Dateien (Files) zusammengefasst und als eine Einheit behandelt.
Datei (File)
− Eine Datei ist nichts anderes als eine Folge von Bytes.
− Zusammengehörige Daten werden zu Dateien zusammengefasst, um sie effizient auf permanente Speichermedien ablegen und wieder lesen zu können. (Æ Magnetspeicher)
− Es können beliebige Arten von Daten gespeichert werden:
−
Text, Zahlen
−
Bilder, Video, Sound
−
Programme, Objekte
− Die Interpretation der Byte-Folge in einer Datei ist Sache des entsprechenden Programms.
Zugriff auf Dateien
Es bestehen grundsätzlich 2 Möglichkeiten, um auf Dateien zuzugreifen:
− sequentieller Zugriff:
−
Die Daten werden wie bei einem Tonband von vorne beginnend nacheinander eingelesen.
−
Dieser Zugriff wird auch als Stream-Zugriff bezeichnet.
−
Dies ist die häufigste Art, auf Dateien zuzugreifen.
− Zugriff in beliebiger Reihenfolge, wie z.B. bei Arbeitsspeicher oder einer CD:
−
Dieser Zugriff wird auch als Random-Zugriff bezeichnet.
−
Random-Zugriff auf Dateien wird nur in speziellen Fällen benötigt, wie z.B. bei Datenbanken.
Zugriffsrechte:
− Normalerweise hat der Benützer uneingeschränkte Zugriffsrechte auf seine Dateien (Lesen,
Schreiben, Umbenennen, Löschen, Ausführen).
− Die Zugriffsrechte können aber vom Besitzer der Datei eingeschränkt werden.
− Applets:
−
Applets erhalten normalerweise vom Browser nur sehr beschränkte Zugriffsrechte auf
Files des Computers, auf dem das Applet läuft (aus Sicherheitsgründen).
−
Signierte Applets oder mit einer entsprechend eingestellten Security-Policy können
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
3
•
Applets jedoch auch auf Files zugreifen.
−
Zugriffe auf Files des Servers, von dem das Applet stammt, sind jedoch erlaubt.
Dateien in Java
− Es existieren über 50 verschiedene Klassen für die verschiedenen Arten von Filezugriffen.
− Die Hierarchie dieser Klassen ist jedoch gut strukturiert und regulär aufgebaut.
1.4
•
•
•
Sequentieller Filezugriff: Streams
Sequentieller Zugriff auf Dateien
− Beim sequentiellen Zugriff wird ein Byte nach dem anderen von vorne beginnend gelesen bzw.
geschrieben.
− Dadurch entsteht beim Lesen und Schreiben ein Datenstrom Æ Stream
− Einen solchen Stream erhält man auch, wenn Daten statt von einem Speichermedium über eine
Kommunikationsleitung übertragen werden.
− Da das Lesen einzelner Bytes von einem Speichermedium sehr langsam im Vergleich zum
Zugriff auf den Arbeitsspeicher ist, werden normalerweise nur ganze Blöcke von Daten vom
Speichermedium gelesen bzw. darauf geschrieben. Die Daten werden dazu im Arbeitsspeicher
zwischengelagert (gepuffert).
Ablauf beim Lesen eines Files:
− File öffnen
− Daten nacheinander einlesen und intern abspeichern
− File schliessen
Ablauf beim Schreiben eines Files:
− File öffnen
− Daten nacheinander auf das File schreiben
− File schliessen
1.4.1
•
•
•
Direkter (byte-weiser) sequentieller Zugriff auf eine Datei
Der direkte byte-weise sequentielle Zugriff auf eine Datei ist in Abbildung 1 dargestellt.
Vorteil des direkten byte-weisen Zugriffs ist die maximale Kontrolle, die der Programmierer über
den Dateizugriff hat, da die Daten ohne Verzögerung auf die Datei geschrieben bzw. von der Datei
gelesen werden.
Die Nachteile sind zum einen, dass dieser Zugriff langsam und umständlich ist. Zudem wird während des Dateizugriffs die Applikation jedesmal blockiert, bis das Byte gelesen ist.
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
4
Direkter Lesezugriff
Direkter Schreibzugriff
Datei
Datei
Arbeitsspeicher
Arbeitsspeicher
Abbildung 1: Direkter sequentieller Zugriff auf eine Datei
1.4.2
•
Gepufferter (blockweiser) Zugriff auf Datei
Der gepufferte blockweise Zugriff auf eine Datei ist in Abbildung 2 dargestellt.
Gepufferter Lesezugriff
Datei
Gepufferter Schreibzugriff
Datei
Block
Block
Arbeitsspeicher
Arbeiten mit
Dateien.doc
Arbeitsspeicher
© 2010 InIT/ZHAW
5
Abbildung 2: Gepufferter blockweiser Zugriff auf eine Datei
•
•
•
•
1.5
•
Ablauf beim Lesen:
− Die Applikation wartet, bis der ganze Puffer gefüllt ist mit Daten von der Datei. Das Füllen des
Puffers ist langsam, da es von der Lesegeschwindigkeit des Speichermediums abhängt. Die Applikation kann in der Zwischenzeit aber etwas anderes machen.
− Die Applikation liest den Puffer (Block) als Ganzes ein. Dieser Vorgang ist sehr schnell, da es
sich im Wesentlichen um das Umkopieren von Daten im Arbeitsspeicher handelt.
Ablauf beim Schreiben:
− Applikation schreibt einen Block Daten in den Puffer.
− Von dort werden die Daten byteweise auf die Datei geschrieben.
− Während dieser Zeit kann die Applikation wiederum weiterarbeiten.
Vorteile:
− Die Applikation ist weniger blockiert durch den Dateizugriff.
− Das blockweise Lesen und Schreiben ermöglicht komfortables Lesen und Schreiben grösserer
Einheiten (ganze Zahl, ganze Zeile, etc.)
Nachteile:
− Die Applikation erhält die Daten normalerweise erst, wenn der Puffer gefüllt ist bzw. die Daten
werden normalerweise erst auf die Datei geschrieben, wenn der Puffer voll ist.
− Auch wenn die Applikation nur ein Byte braucht, wird ein ganzer Block von der Datei gelesen.
Java IO-Klassen
Die folgende Liste zeigt die wichtigsten Klassen für das Lesen und Schreiben von Daten auf Dateien
und andere Medien (Bildschirm, Tastatur, Übertragungskanäle) in der Klassenhierarchie:
InputStream
- FileIntputStream
Lesen von beliebigen Daten (Byte-Streams)
Lesen beliebiger Daten von Files
OutputStream
- FileOutputStream
Schreiben beliebiger Daten (Byte-Stream)
Schreiben beliebiger Daten auf Files
Reader
Lesen von Character-Streams (Zeichenströme)
(besitzt analoge Subklassen wie InputStream)
gepuffertes Lesen, ermöglicht auch zeilenweises Lesen
liest Bytes und wandelt sie in Characters um
liest Character-Streams von einem File
- BufferedReader
- InputStreamReader
- FileReader
Writer
- PrintWriter
- OutputStreamWriter
- FileWriter
Arbeiten mit
Dateien.doc
Schreiben von Character-Streams
(analoge Subklassen zu OutputStream)
schreibt beliebige Java-Objekte als Strings einen OutputStream
wandelt Characters in Bytes und gibt sie aus
schreibt Character-Streams auf ein File.
© 2010 InIT/ZHAW
6
1.5.1
•
Erklärungen zu den einzelnen Klassen für das Lesen / Schreiben von Daten:
FileInputStream, FileOutputStream
Mit diesen Klassen werden einzelne Bytes von einer Datei gelesen (mit der Methode read()) oder
geschrieben (mit write()) wie in Abbildung 3 dargestellt.
Datei
Datei
FileInputStream
FileOutputStream
Bytes
Bytes
read()
write()
Abbildung 3: FileInputStream und FileOutputStream
•
InputStreamReader, OutputStreamWriter
−
−
−
−
−
Mit diesen Klassen können Byte-Streams in Character-Streams umgewandelt werden (siehe
Abbildung 4)
Die bei der Umwandlung verwendete Codierung kann gewählt werden.
Um Character-Streams von einer Datei zu lesen, müssen diese zuerst mit einem FileInputStream gelesen werden.
Um Character-Streams auf eine Datei zu schreiben, müssen die nach der Umwandlung in ByteStreams mit einem FileOutputStream geschrieben werden.
Die Klassen InputStreamReader und OutputStreamWriter werden auch verwendet,
um auf andere Ein-/Ausgabe-Medien zuzugreifen, z.B. Tastatur, Bildschirm oder Internet (lesen
und schreiben von Web-Seiten).
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
7
Datei
Datei
FileInputStream
FileOutputStream
Bytes
Bytes
InputStreamReader
Chars
OutputStreamWriter
Chars
read()
write()
Abbildung 4: Byteweises Lesen und Schreiben einer Datei mit Umwandlung von Bytes zu Characters
1.5.2
•
•
•
Lesen und Schreiben von Textdateien
Die häufigste Art von Dateien, die gelesen und geschrieben werden, sind Textdateien.
Diese werden meistens zeilenweise gelesen/geschrieben.
Aus diesem Grund gibt es spezielle Klassen, um einfach auf solche Textdateien zuzugreifen:
−
Klasse FileReader
liest einzelne Characters von einer Datei. Dabei wird die Standard-Codierung für die Umwandlung von Bytes zu Characters verwendet.
−
Klasse BufferedReader
Liest Characters aus einer Textdatei einzeln oder zeilenweise:
− read()
liest nächsten Character aus der Datei.
− readLine()
liest nächste Zeile aus Datei.
−
Klasse FileWriter
schreibt einzelne Characters in eine Datei. Dabei wird die Standard-Codierung für die
Umwandlung von Characters zu Bytes verwendet.
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
8
−
•
Klasse PrintWriter
Schreibt Characters in eine Textdatei, einzeln oder zeilenweise:
− print(String str)
hängt String str ans Ende der Datei
− println(String str)
hängt String str ans Ende der Datei, und fügt eine
Zeilenendemarke ('\n') an.
− analoge print-Methoden existieren für int-, float- , ..., doubleZahlen und ganze Objekte.
Direktes Lesen und Schreiben von Textdateien
− Textdateien können direkt zeichenweise gelesen und beschrieben werden mit den Klassen FileReader und FileWriter (siehe Abbildung 5).
FileReader
FileWriter
Datei
Datei
Bytes
Bytes
FileReader
FileWriter
Chars
Chars
read()
write()
Abbildung 5: Zeichenweises Lesen und Schreiben einer Datei
•
Gepuffertes Lesen und Schreiben von Textdateien ermöglichen die Klassen BufferedReader und
PrintWriter (siehe Abbildung 6).
•
Beachten Sie:
− Viele Methoden, die Streams bearbeiten, können IOExceptions werfen.
− Diese sind checked, d.h, sie müssen abgefangen werden.
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
9
BufferedReader
PrintWriter
Datei
Datei
Bytes
Bytes
FileReader
FileWriter
Chars
Chars
BufferedReader
PrintWriter
Chars
Chars
read()
readln()
print("Hello")
print(5)
print(true)
Abbildung 6: Gepuffertes Lesen und Schreiben von Textdateien
1.6
•
Ausgabe auf ein File
Beispiel: FileDemo1
Dieses Beispielprogramm demonstriert, wie ein Text in einer TextArea auf eine Datei geschrieben
werden kann mittels einem PrintWriter-Objekt.
Abbildung 7: FileDemo1
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
10
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class FileDemo1 extends Frame implements WindowListener,
ActionListener {
private TextArea inputTextArea;
private Button saveButton;
private PrintWriter outFile;
public static void main (String [] args) {
FileDemo1 demo = new FileDemo1();
demo.setSize(300,400);
demo.setVisible(true);
}
public FileDemo1() {
saveButton = new Button("save");
add (saveButton, BorderLayout.NORTH);
saveButton.addActionListener(this);
inputTextArea = new TextArea(10,50);
add (inputTextArea, BorderLayout.CENTER);
addWindowListener(this);
//for windowClosing
}
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == saveButton ) {
try{
outFile = new PrintWriter(new FileWriter("testout.txt"),
true);
outFile.print( inputTextArea.getText() );
outFile.close();
}
catch (IOException e) {
System.err.println("File Error: " + e.toString() );
System.exit(1);
}
}
}
public void windowClosing(WindowEvent e) {
System.exit(0);
}
//empty WindowListener Methods
public void windowIconified(WindowEvent e) { }
public void windowOpened(WindowEvent e) { }
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
11
public
public
public
public
void
void
void
void
windowClosed(WindowEvent e) { }
windowDeiconified(WindowEvent e) { }
windowActivated(WindowEvent e) { }
windowDeactivated(WindowEvent e) { }
}
Programm 1: FileDemo1
•
Erklärungen zum Programm
− Als erstes muss ein PrintWriter-Objekt deklariert werden:
private PrintWriter outFile;
−
Beim Erzeugen des PrintWriter-Objektes wird sodann das entsprechende File neu erzeugt:
outFile = new PrintWriter( new FileWriter("filename.ext"), true);
−
−
−
−
−
−
−
Diese Anweisung öffnet das File mit Namen "filename.ext".
Falls es noch nicht existiert, wird es erzeugt.
Falls es existiert, wird es überschrieben.
Der verwendete Konstruktor des PrintWriters verlangt 2 Argumente:
− Ein Writer-Objekt. In der obigen Anweisung wird direkt ein FileWriter-Objekt
erzeugt mit new und dieses dem PrintWriter-Konstruktor übergeben. Der Konstruktor von FileWriter verlangt seinerseits den gewünschten Namen des zu erzeugenden Files.
− Das 2. Argument besagt, dass der Puffer (Zwischenspeicher) am Ende jeder Zeile auf
das File geschrieben werden soll (autoflush = true).
−
Die erzeugte PrintWriter-Instanz wird schliesslich der Variablen outfile zugewiesen.
−
Ab Java 5 kann das PrintWriter-Objekt auch direkt mit dem Filenamen erzeugt
werden:
outfile = new PrintWriter("filename.ext");
Beachten Sie:
Der Name des Files "filename.ext" ist der Name, wie er im Directory erscheint.
Æ Dieser ist unabhängig vom Variablennamen outfile des PrintWriter-Objekts.
PrintWriter ist ein Spezialfall:
−
Diese Klasse wirft keine Exceptions.
−
Um herauszufinden, ob beim Schreiben der Datei ein Fehler aufgetreten ist, muss der
Programmierer die Methode outfile.checkError() aufrufen. (Wird in diesem Beispiel nicht gemacht)
Nach dem Erzeugen und Öffnen der Datei, werden die Daten hineingeschrieben mit:
outFile.print(String str);
−
−
Diese Anweisung schreibt den String str auf die Datei outFile.
Nachdem das Schreiben der Datei fertig ist, muss das File wieder geschlossen werden mit:
outFile.close();
−
Arbeiten mit
Diese Anweisung schliesst das File outfile.
Dateien.doc
© 2010 InIT/ZHAW
12
1.7
•
Einlesen von einem File
Beispiel: FileDemo2
Das Beispielprogramm FileDemo2 zeigt, wie aus einem File gelesen und in eine TextArea geschrieben werden kann.
Abbildung 8: FileDemo2
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class FileDemo2 extends Frame implements WindowListener,
ActionListener {
private TextArea inputTextArea;
private Button loadButton;
private BufferedReader inFile;
private TextField nameField;
public static void main (String [] args) {
FileDemo2 demo = new FileDemo2();
demo.setSize(300,400);
demo.setVisible(true);
}
public FileDemo2() {
Panel top = new Panel();
loadButton = new Button("load");
top.add(loadButton);
loadButton.addActionListener(this);
nameField = new TextField(20);
top.add(nameField);
nameField.addActionListener(this);
add ("North", top);
inputTextArea = new TextArea("",10,50);
add ("Center", inputTextArea);
addWindowListener(this);
}
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
13
public void actionPerformed(ActionEvent evt) {
String fileName;
String line;
if (evt.getSource() == loadButton) {
fileName = nameField.getText();
try {
inFile = new BufferedReader(new FileReader(fileName));
inputTextArea.setText("");
// clear the input area
while( ( line = inFile.readLine() ) != null) {
inputTextArea.append(line+"\n");
}
inFile.close();
}
catch (IOException e) {
System.err.println("Error in file " + fileName +
": " + e.toString() );
System.exit(1);
}
}
}
public void windowClosing(WindowEvent e) {
System.exit(0);
}
//empty WindowListener Methods
. . .
}
Programm 2: FileDemo2
•
Erklärungen zum Programm
− Um die Textdatei zu Lesen wird zuerst ein BufferedReader-Objekt deklariert:
private BufferedReader inFile;
− Das BufferedReader-Objekt wird mit folgender Anweisung geöffnet:
inFile = new BufferedReader(new FileReader(filename));
−
Konstruktor von BufferedReader verlangt ein Reader-Objekt.
−
Dieses wird mit new FileReader(fileName) erzeugt und gerade übergeben.
Dabei wird die Datei fileName geöffnet.
−
Wird die Grösse des Lesepuffers nicht angegeben, so ist sie standardmässig 8K Zeichen.
− Die Daten werden nach dem Öffnen der Datei zeilenweise eingelesen mit folgender Schleife:
while ((line = inFile.readLine()) != null){
inputTextArea.append(line+"\n");
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
14
}
−
−
−
−
−
readLine gibt die gelesene Zeile (ohne \n) zurück oder null, falls das Ende des
Files (EOF) erreicht worden ist.
Die gelesene Zeile wird der Variable line zugewiesen.
Danach wird line im while-Schleifenkopf auf null getestet.
Klammern sind notwendig, damit die Zuweisung zu line vor dem Vergleich erfolgt.
Die Schleife könnte ausführlich auch so geschrieben werden:
line = inFile.readLine();
while (line != null){
inputTextArea.append(line+"\n");
line = inFile.readLine();
}
−
Das File muss nach dem Lesen geschlossen werden mit der Anweisung:
inFile.close();
1.8
•
File durchsuchen
Beispiel: FileSearch
Gegeben sei eine Datei "Punktzahlen.dat", die mit einem gewöhnlichen Editor erstellt worden ist.
Der Inhalt der Datei sieht folgendermassen aus
Muster, 10, 13
Meier, 40, 36
Müller, 34, 19
•
Aufgabe:
Analysieren Sie das Programm FileSearch (Programm 3). Beschreiben Sie in Stichworten,
wie das Programm genau abläuft. Die Bildschirmdarstellung des Programms ist in unten stehender
Abbildung 9 dargestellt.
Abbildung 9: Bildschirmdarstellung des Programms FileSearch
•
Ihre Erklärungen:
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
15
import
import
import
import
java.io.*;
java.awt.*;
java.util.*;
java.awt.event.*;
// StringTokenizer
public class FileSearch extends Frame implements ActionListener,
WindowListener {
private BufferedReader inFile;
private Button searchButton;
private TextField result1Field, result2Field, personField;
private TextField fileNameField, errorField;
private String fileName;
public static void main (String [ ] args) {
FileSearch search = new FileSearch();
search.setSize(400,400);
search.setVisible(true);
}
public FileSearch() {
setLayout(new FlowLayout() );
errorField= new TextField("Type the File name:");
errorField.setEditable(false);
add(errorField);
fileNameField = new TextField(20);
fileNameField.setText("");
add(fileNameField);
searchButton = new Button("Search");
add(searchButton);
searchButton.addActionListener(this);
add(new Label("Type Name:"));
personField = new TextField(20);
personField.setText("");
add(personField);
add( new Label("Result1:"));
result1Field = new TextField(5);
result1Field.setEditable(false);
add(result1Field);
add (new Label("Result2:"));
result2Field= new TextField(5);
result2Field.setEditable(false);
add(result2Field);
this.addWindowListener(this);
}
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
16
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == searchButton) {
fileName = fileNameField.getText();
try {
inFile = new BufferedReader(new FileReader(fileName));
}
catch (IOException e) {
errorField.setText("Can't find file ");
return;
}
errorField.setText("Type the file name:");
// now read the file
try {
String line;
boolean found = false;
while (( ( line = inFile.readLine() ) != null)
&& (! found)) {
// tokens split on commas, spaces
StringTokenizer tokens = new StringTokenizer(line,"
,");
String nameInFile = tokens.nextToken();
if (personField.getText().equals(nameInFile)) {
found = true;
result1Field.setText(tokens.nextToken() );
result2Field.setText(tokens.nextToken() );
}
}
inFile.close();
}
catch (IOException e) {
System.err.println("Error reading file " +
fileName+": " + e.toString() );
System.exit(1);
}
}
}
// WindowListener methods - all needed!
public void windowClosing(WindowEvent e) {
System.exit(0);
}
//empty WindowListener Methods ...
}
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
17
Programm 3: FileSearch
1.9
•
Filenamen aussuchen
Heutzutage ist es üblich, dass der Benützer eines Programms beim Öffnen oder Abspeichern einer
Datei ein Auswahlwahlfenster, wie unten abgebildet, mit den vorhandenen Dateien bekommt. Dieses
Dialogfenster wird in Java durch die Klasse FileDialog zur Verfügung gestellt.
Abbildung 10: Programm FileDialogDemo
•
Beispiel: FileDialogDemo
In diesem Beispielprogramm wird gezeigt, wie ein FileDialog-Fenster erzeugt und wie die Auswahl
des Benützers vom Programm aus abgefragt werden kann.
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class FileDialogDemo extends Frame implements
ActionListener, WindowListener {
private Button loadButton;
private FileDialog getNameBox;
private TextField nameField;
public static void main (String [] args) {
FileDialogDemo demo = new FileDialogDemo();
demo.setSize(500,400);
demo.setVisible(true);
}
public FileDialogDemo() {
setLayout( new FlowLayout() );
loadButton = new Button("load");
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
18
add(loadButton);
loadButton.addActionListener(this);
nameField = new TextField(30);
add(nameField);
addWindowListener(this);
// for windowClosing
}
public void actionPerformed(ActionEvent evt) {
String fileName, dirName;
if (evt.getSource() == loadButton) {
getNameBox = new FileDialog(this, "get Name",
FileDialog.LOAD);
getNameBox.setVisible(true);
// display the name
fileName = getNameBox.getFile();
dirName = getNameBox.getDirectory();
nameField.setText(dirName + fileName);
}
}
public void windowClosing(WindowEvent e) {
System.exit(0);
}
//empty WindowListener Methods
...
}
Programm 4: FileDialogDemo
•
Erklärungen zum Programm
− Deklaration einer File-Dialogbox:
private FileDialog getNameBox;
−
Erzeugen der Dialogbox:
getNameBox = new FileDialog(this,"get a name",FileDialog.LOAD);
getNameBox.setVisible(true);
−
−
Parameter des Konstruktors:
− Frame, in dem die Dialog-Box erzeugt wird
− Titel der Dialog-Box
− Dialog-Art
– FileDialog.LOAD Æ um eine Datei zu öffnen
– FileDialog.SAVE Æ um eine Datei zu speichern
−
es wird ein sogenannter modaler Dialog erzeugt, d.h.:
sobald setVisilble(ture) aufgerufen wird, blockiert das Programm, bis eine Datei
ausgewählt wurde.
Den Namen des ausgewählten Files oder das aktuelle Verzeichnis erhält man mit den Anweisungen:
String fileName, dirName;
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
19
fileName = getNameBox.getFile();
dirName = getNameBox.getDirectory();
1.10 Umgang mit Dateien
•
Um Dateien als Ganzes zu bearbeiten, stellt Java die Klasse File zur Verfügung.
•
Da Directories ebenfalls (spezielle) Files sind, können auch Directories mit dieser Klasse bearbeitet
werden.
Instanzierung eines File-Objektes:
•
File myFile = new File(String absFilename);
−
−
−
•
erzeugt ein File-Objekt, nicht das File selbst.
absFilename: Filename samt absolutem Pfad (z.B. c:\java\demo.java)
Um einen einzelnen Backslash ("\") in einem String einzugeben, schreibt man zwei Backslash nacheinander Æ "\\"
Methoden der Klasse File:
String getPath()
String getAbsolutePath()
boolean exists()
boolean isDirectory()
boolean isFile()
boolean canRead()
boolean canWrite()
boolean delete()
long length()
String[] list()
long lastModified()
boolean mkdir()
boolean renameTo(File name)
gibt relativen Pfad des Files zurück
gibt absoluten Pfad des Files zurück
Testet, ob File existiert oder nicht
Testet, ob File ein Directory ist
Testet, ob es ein File (kein Directory) ist
gibt false zurück, falls File nicht existiert
ist Lesen des Files erlaubt
ist Schreiben erlaubt
Löscht das File. Gibt true zurück, falls File
erfolgreich gelöscht wurde
gibt die File-Grösse in Bytes zurück
gibt Liste von Filenamen des Directory zurück
(null falls File kein Directory ist)
Zeitpunkt der letzten Modifikation
(Zeit in ms seit Zeitursprung, nur für Vergleiche
geeignet)
erzeugt ein Verzeichnis anhand des Pfades des
Files
benennt File um zu name. Gibt true zurück, falls
erfolgreich
1.11 Konsolen-IO
•
Historische Entwicklung von User-Interfaces:
− Am Anfang gab es nur Terminals/Konsolen
−
Die Ein-/Ausgabe war dementsprechend nur textbasiert möglich:
− Eingabe per Keyboard
− Ausgabe auf Bildschirm
− Später kamen einfache Menus dazu:
−
Diese boten eine Auswahlmöglichkeit aus einer Befehls-Liste via Cursor-Tasten.
− Schliesslich kamen die graphischen Benützerschnittstellen (GUI) auf:
−
Diese bieten neue zusätzliche Eingabenmöglichkeiten per Maus.
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
20
•
Software mit Kommandozeilen-Interface sind aber auch heute noch verbreitet
− Sie ermöglichen bespielsweise, dass der Output von einem Programm in einem anderen als Input verwenden werden kann (sogenanntes Piping).
− Es erlaubt auch das Ausführen von Skripts, und damit das Automatisieren von sich wiederholenden Tasks (z.B. Backup).
− Dieses Interface wird häufig auch von Systemadministration gebraucht.
− In Java sind Kommandozeilen-Interfaces einfach realisierbar mit speziellen Streams, die in der
System-Klasse bereits vordefiniert sind.
1.12 System-Klasse
•
•
•
•
•
Die System-Klasse stellt eine plattformunabhängige Schnittstelle zu den Betriebssystemfunktionen zur Verfügung.
Sie enthält nur Klassenvariablen und Klassenmethoden.
Sie kann nicht instanziert werden.
Sie stellt unter anderem 3 Streams zur Verfügung:
− System.in
− System.out
− System.err
System.in
−
−
•
Dies ist ein InputStream-Objekt. Es muss nicht erzeugt werden, sondern existiert schon.
Es kann für das direkte Einlesen von Tastatureingaben verwendet werden, aber auch für Inputs
von Pipes.
− Mit dem Objekt System.in kann der Programmierer nur byteweise Daten einlesen.
Das Einlesen von der Kommandozeile geht viel komfortabler mit einem BufferedReader:
private BufferedReader keyboard;
keyboard = new BufferedReader( new InputStreamReader(System.in),1);
−
−
Die Puffergrösse des BuffereReader wird normalerweise auf 1 gesetzt Æ So entsteht keine
Verzögerung nach dem Drücken der Return-Taste. BufferedReader wartet nämlich bei einem readLine() bis der Puffer voll ist, bevor er den ganzen Puffer weitergibt.
Nachdem der BufferedReader aufgesetzt ist, kann man zeilenweise von der Tastatur lesen mit:
String line = keyboard.readLine();
•
System.out
− ist ein PrintStream-Objekt (Diese Klasse wurde ab Java 1.1 abgelöst durch die Klasse
PrintWriter).
− Das System.out-Objekt muss ebenfalls nicht erzeugt werden, weil es bereits existiert.
− Methoden
−
print(String str), println(String str)
str kann auch mehrere Zeilen enthalten
−
Analoge print-Methoden existieren für int, float, ... und für Objekte
−
System.out.flush() gibt Puffer nach einem print-Befehl auf den Bildschirm
aus, auch wenn Puffer noch nicht voll ist.
Dies braucht man, wenn das Programm einen Prompt an den Benützer ausgeben will und
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
21
−
•
•
der Benützer die Eingabe gerade nach dem Prompt auf der gleichen Zeile machen soll.
Dieser Stream kann ebenfalls umgeleitet werden (Piping).
System.err: gleich wie System.out, aber dieser Stream kann nicht umgeleitet werden.
System.exit(int errorCode):
−
−
beendet Programm augenblicklich und gibt errorCode an Betriebssystem zurück.
Konvention für errorCode:
−
0
Normale Beendigung
−
!= 0 Abbruch nach Fehler
1.13 Konsolenbasierte Anwendungen
•
Beispielprogramm: Finder
Das Programm Finder sucht einen String in einer Datei. Es gibt alle Zeilen aus, wo der gesuchte
String vorkommt zusammen mit der vorherigen und der nachfolgenden Zeile (siehe Abbildung 11).
− Der Filename wird als Command-Line-Argument übergeben
− Für das Einlesen des gesuchten Strings erzeugt das Programm in der Methode prompt(...)
einen Prompt mit einer entsprechenden Meldung. Der Benützer kann dann, den gesuchten
String nach dem Prompt eingeben.
− In der Figur ist der Dialog mit dem Programm dargestellt, um den String „if“ in der Datei „Finder.java“ zu suchen. Es wurden zwei Zeilen mit „if“ gefunden.
Abbildung 11: Finder
import java.io.*;
public class Finder {
private String line1, line2, line3;
private BufferedReader keyboard, inStream;
public static void main (String []
Finder aFind = new Finder();
aFind.doSearch(args[0]);
}
args) {
private void doSearch(String fileName) {
keyboard = new BufferedReader(new
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
22
InputStreamReader(System.in),1);
String wanted = prompt("Type string to find:");
line1 = "";
line2 = "";
try {
inStream = new BufferedReader(new FileReader(fileName));
while ((line3 = inStream.readLine()) != null) {
if ( line2.indexOf(wanted) >= 0 ){ displayLine(); }
// advance to the next group of 3
line1 = line2;
line2 = line3;
// and get new line3 from file...
}
//check the last line
line3 = "";
//replace null value with ""
if (line2.indexOf(wanted) >= 0){
displayLine();
}
inStream.close();
}
catch (IOException e) {
System.err.println("Error in Finder: " + e.toString());
System.exit(1);
}
}
private void displayLine() {
System.out.println("<<------------ context:");
System.out.println(line1);
System.out.println(line2);
System.out.println(line3);
System.out.println("
------------->>");
System.out.println("");
}
private String prompt(String message) {
String reply = "";
try {
System.out.print(message);
System.out.flush();
reply = keyboard.readLine();
}
catch (IOException e) {
System.out.println("Keyboard input " +
System.exit(2);
}
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
e.toString() );
23
return reply;
}
}
Programm 5: Finder
1.14 Lesen von anderen Sites
•
•
•
Das Lesen von anderen Sites geht so leicht wie das Lesen von lokalen Files.
Was es dazu braucht, ist die Angabe des URL (Uniform Resource Locator) des gewünschten Files.
Beispiel: TinyBrowser
− Das Programm TinyBrowser stellt eine HTML-Seite als Text dar (Abbildung 12)..
− Das Programm fragt vom Benützer eine URL ab. Es nimmt dann Verbindung auf zu dieser URL
und gibt den Text, der von dort geschickt wird (z.B. eine HTML-Seite), auf die Konsole aus.
Abbildung 12: TinyBrowser
Figur 27:
import java.io.*;
import java.net.*;
public class TinyBrowser
{
private BufferedReader inStream, keyboard;
public static void main (String [] args) {
TinyBrowser aBrowser = new TinyBrowser();
aBrowser.fetch();
}
private void fetch() {
String urlString = "";
String line;
keyboard = new BufferedReader(new
InputStreamReader(System.in),1);
try {
urlString = prompt (" Type a URL address (e.g
http://java.sun.com/) :");
// create a link to a URL
URL urlAddress = new URL(urlString);
URLConnection link = urlAddress.openConnection();
inStream = new BufferedReader(new
InputStreamReader(link.getInputStream()));
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
24
while ((line = inStream.readLine()) != null) {
System.out.print(line);
}
} // try
catch (MalformedURLException e) {
System.err.println(urlString + e.toString());
System.exit(2);
}
catch (IOException e) {
System.err.println("Error in accessing URL: "+
e.toString());
System.exit(1);
}
} // fetch
private String prompt(String message) {
String reply = "";
try {
System.out.print(message);
System.out.flush();
reply = keyboard.readLine();
}
catch (IOException e) {
System.out.println(" Keyboard input " + e.toString() );
System.exit(2);
}
return reply;
}
}
Programm 6: TinyBrowser
•
Methoden im Zusammenhang mit URLs (dazu muss java.net.* importiert werden):
−
URL urlAddress = new URL(urlstring);
−
−
−
−
URLConnection link = urlAddress.openConnection();
−
−
gibt den gewünschten URL zurück.
urlstring hat die Form Protokollname:Ressourcenname
In Java 2 werden die Protokolle http und ftp unterstützt.
öffnet die Verbindung zur gewünschten Ressource.
link.getInputStream()
−
−
Arbeiten mit
gibt einen InputStream zurück, der von der geöffneten Verbindung Daten einliest.
Dieser Stream wird mit dem InputStreamReader verarbeitet.
Dateien.doc
© 2010 InIT/ZHAW
25
1.15 Persistente Objekte
•
•
•
•
•
•
•
•
•
•
Bei fast jeder realen Applikation besteht das Bedürfnis, den aktuellen Zustand der Applikation abspeichern zu können.
Da der Zustand eines objektorientierten Programms in Objekten dezentral gespeichert ist, läuft obige
Forderung darauf hinaus, Objekte persistent abspeichern und wieder laden zu können.
Zu diesem Zweck bietet Java die Möglichkeit, Objekte in einen Byte-Stream zu verwandeln, der
dann zum Beispiel auf eine Datei gespeichert werden kann. Dieser Vorgang wird Serialisierung (serialization) eines Objekts genannt.
Der abgespeicherte Byte-Stream kann später wieder eingelesen und daraus wieder ein identisches
Objekt erzeugt werden Dieser Vorgang wird Deserialisierung (dezerialization) genannt.
Bei der Serialisierung eines Objekts werden alle notwendigen Attribute in einen Byte-Stream verpackt.
Falls ein Attribut eine Referenz auf eine zweites Objekt darstellt, wird auch dieses Objekt serialisiert
und in den Byte-Stream gepackt. Dies führt dazu, dass mit der Serialisierung eines Objekts ganze
Objekt-Graphen serialisiert werden.
Die Serialisierung von Objekten dient nicht nur zum Speichern von Objekten. Damit können Objekte
auch über Kommunikationsleitungen (z.B. Internet) übertgragen werden.
Für die Serialisierung stellt Java den ObjectOutputStream zur Verfügung. Dieser verfügt über
die Methode writeObject(Object obj), mit der Objekte direkt serialisiert werden können.
Für das Einlesen eines serialisierten Objekts stellt die Klasse ObjectInputStream die Methode
Object readObject() zur Verfügung.
Damit ein Objekt serialisierbar ist, muss es das Interface Serializable implementieren. Dieses
Interface verlangt keine Methoden, die implementiert werden müssen. Es zeigt lediglich an, dass das
entsprechende Objekt für die Serialisierung geeignet ist.
Beispiel:
− Das Programm 7: BankApplikation zeigt einen Auszug aus der Klasse BankApplikation.
Dieser enthält zwei Methoden um jedes Konto im Konto-Array konto einzeln abzuspeichern
und wieder zu lesen.
− Um die Konto-Objekte abzuspeichern, wird zuerst das FileOutputStream-Objekt
ostream erzeugt. Dadurch wird eine neue Datei filename im Verzeichnis dirname angelegt.
− Mit Hilfe des FileOutputStream-Objekts ostream wird sodann ein ObjectOutputStream-Objekt erzeugt.
− In einer Schleife wird schliesslich ein Konto-Objekt nach den anderen in den Object-Stream und
damit auf die Datei geschrieben mit Hilfe der Methode writeObject(Object obj) des
ObjectStream-Objekts. Da der Parameter dieser Methode vom Typ Object ist, können der
Methode Objekte beliebiger Klassen übergeben werden.
− Beim Laden der Objekte wird ein FileInputStream-Objekt erzeugt, dem ein ObjectInputStream-Objekt im Konstruktor übergeben wird.
− Danach kann in einer Schleife wieder ein Objekt nach dem anderen mit Hilfe der vom ObjectInputStream-Objekt zur Verfügung gestellten Methode Object readObject() eingelesen werden.
− readObject() gibt als Resultat ein Objekt vom Datentyp Object zurück. Dieses muss nun
zuerst wieder in ein Objekt der gewünschten Klasse (Konto) zurückgewandelt werden.
− Damit ist die Wiederherstellung des Konto-Objekts abgeschlossen.
import ...
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
26
public class BankApplikation extends Frame implements
ActionListener, WindowListener {
private Konto[] konto = new Konto[20];
...
// speichert alle Kontos des Arrays konto
public void kontosSpeichern(String dirname, String filename){
try{
FileOutputStream ostream = new FileOutputStream(dirname +
filename);
ObjectOutputStream objstream = new ObjectOutputStream(ostream);
for (int i= 0; i < konto.length; i++){
objstream.writeObject(konto[i]);
}
objstream.flush();
ostream.close();
}
catch (IOException e){
System.out.println("Error while saving accounts");
e.printStackTrace();
}
}
// lädt alle Kontos des Arrays konto
public void kontosLaden(String filename, String dirname){
try{
FileInputStream instream = new FileInputStream(dirname +
filename);
ObjectInputStream objstream = new ObjectInputStream(instream);
for (int i=0; i<konto.length;i++){
konto[i] = (Konto)objstream.readObject();
}
instream.close();
}
catch (IOException e){
System.out.println("Error while loading accounts");
e.printStackTrace();
}
catch (ClassNotFoundException e){
System.out.println("Error while loading accounts");
e.printStackTrace();
}
}
}
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
27
Programm 7: BankApplikation
−
Da beim Serialisieren eines Objekts auch alle damit verbundenen Objekte ebenfalls serialisiert
werden, können die Kontos im Progamm Fehler! Verweisquelle konnte nicht gefunden werden. auch einfach abgespeichert werden, indem der Konto-Array konto als Ganzes serialisiert wird. Damit werden automatisch auch alle Konto-Objekte mit serialisiert. Die entsprechenden Methoden sind im Programm Fehler! Verweisquelle konnte nicht gefunden werden. aufgelistet.
public class BankApplikation extends Frame implements
ActionListener, WindowListener {
private Konto[] konto = new Konto[20];
...
// speichert Konto-Array konto
public void kontoArraySpeichern(String dirname, String filename){
try{
FileOutputStream ostream = new FileOutputStream(dirname +
filename);
ObjectOutputStream objstream =
new ObjectOutputStream(ostream);
objstream.writeObject(konto);
objstream.flush();
ostream.close();
}
catch (IOException e){
System.out.println("Error while saving accounts");
e.printStackTrace();
}
}
// lädt Konto-Array konto
public void kontoArrayLaden(){
try{
FileInputStream instream = new FileInputStream(dirname +
filename);
ObjectInputStream objstream = new
ObjectInputStream(instream);
konto = (Konto[])objstream.readObject();
instream.close();
}
catch (IOException e){
System.out.println("Error while loading accounts");
e.printStackTrace();
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
28
}
catch (ClassNotFoundException e){
System.out.println("Error while loading accounts");
e.printStackTrace();
}
}
Programm 8: Speichern des Konto-Arrays als Ganzem
1.16 Professionelles Programmieren
Arbeiten mit
Dateien.doc
© 2010 InIT/ZHAW
29
Herunterladen