Kap.8: DATEIEN (File, Stream)
Kapitel-Index
8.1 Text -Datei
8.2 Binär-Datei
8.3 Testfragen
Eine Datei ist eine eindimensionale Kette wachsender, nicht begrenzter Länge aus Daten, die sämtlich vom Typ char (für Java-Zeichen) oder sämtlich vom Typ byte (herkömmlich) sind, mit einer jeweils nach dem Lesen oder Schreiben automatisch weitergesetzten
Position.
------
------
-------
------
------
char-Datei
char
char
. . .
char
------
------
-------
------
------
Position
------
------
-------
------
------
byte-Datei
byte
byte
. . .
byte
------
------
-------
------
------
Position
Es gibt im Programm vereinbarte interne Dateien und außerhalb
des Programms im System vereinbarte externe Dateien. Zur Ein/Ausgabe zwischen internen und externen Dateien steht im Paket java.io
eine Vielfalt von Klassen zur Verfügung.
---------------------------------------------------------
Klasse
-----------
-----------
Datei-Zustand Daten-Fluß
File...
-----------
-----------
Text-Datei Binär-Datei
LineNumber... ...Stream...
-----
-----
-----
-----
Lesen Schreiben Lesen Schreiben
...Reader ...Writer ...Reader ...Writer
z.B. (8.1) z.B. (8.2)
LineNumberReader OutputStreamWriter
---------------------------------------------------------
Fig. 8: Namensgebung für Klassen in java.io
Die Vielfalt von Klassen in java.io ist dadurch zu erklären,
dass Java maschinenunabhängig Dateieffekte erzielt, die sonst auf
verschiedenen Maschinen mit verschiedenen Dateikonzepten verwirklicht werden.
"File"-Klassen ermöglichen Setzen oder Abfragen von Existenz,
Pfad, Name, Länge und Zugriffsrecht einer Datei (englisch file).
"LineNumber"- und "Stream"-Klassen ermöglichen Hin- oder Rückfluß
(englisch stream) von Daten zwischen interner und externer Datei.
Für Applikationen, die Dateien zeichenweise abarbeiten, reichen
"Stream"-Klassen aus. Für Applikationen mit gepufferter Dateiverarbeitung verwendet man "File"- und "Stream"-Klassen kombiniert.
Applets ist der Zugriff auf Dateien aus Sicherheitsgründen verwehrt.
"LineNumber"-Klassen ermöglichen die Verarbeitung von Text-Dateien (8.1), deren Elemente als Text-Zeichen lesbar sind und wo das
Zeichen mit der Nummer 10 Zeilenvorschub bedeutet, d.h. eine Zeilennumerierung möglich ist. "Stream"-Klassen ermöglichen die Verarbeitung von Binär-Dateien (8.2), deren Elemente interne Daten
repräsentieren und wo das Zeichen mit der Nummer 10 nicht notwendig Zeilenvorschub bedeutet.
"Reader"-Klassen ermöglichen die Voreinstellung auf Datei-Lesen,
"Writer"-Klassen auf Datei-Schreiben. Der "fliegende Wechsel"
zwischen Lesen und Schreiben ist im allgemeinen nicht möglich.
Dateien werden im allgemeinen sequentiell "von links nach rechts"
abgearbeitet. Das Voransetzen auf die Folgeposition ist ein Seiteneffekt von
read() Methode für Datei-Lesen ,
write() Methode für Datei-Schreiben .
Die aktuelle Datei-Position wird markiert und die Maximalzahl
limit der zu lesenden Bytes wird notiert mit
mark(int) Methode für Datei-Markieren und Limitieren .
Zurücksetzen auf den markierten Anfang ist möglich mit
reset() Methode zum Datei-Zurücksetzen .
Die aus anderen Programmiersprachen bekannte open() Methode zum
Datei-Öffnen wird in Java ersetzt durch entsprechende File/StreamKonstruktoren. Die aus anderen Programmiersprachen bekannte
close() Methode zum Datei-Schließen
ist auch in Java verfügbar.
"LineNumber"-Klassen (Fig. 8) ermöglichen die Verarbeitung von
Text-Dateien. Auch direkte Verarbeitung mit einem Editor ist möglich.
-----------------------
VVVVVVVVVVVVVVVVVV
Text-Datei (Prinzip)
VVVVVVVVVVVVVVVVVV
VV
-----------------------
VV
VV VV
VV Text-Dateien bestehen aus editierbaren Text-Zeichen und VV
VV sind durch Zeilenbegrenzer fest in Zeilen unterteilt. VV
VV Die Länge der Zeilen hängt jedoch von dem jeweils VV
VV verwendeten Text-Editor und seinen Trimm-Effekten ab. VV
VV VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Zeilenbegrenzer (syntaktisch LineTerminator) sind nach Syntaxdiagram 002 von der Form
002: LineTerminator ZeilenBegrenzer
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H H
H -
-
carriage return CR -
-----------------
H
H
(char)13
H
H
H
H
----------------------
-
line feed LF -
-
H
H (char)10 H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
Welche Zeichenfolge im betreffenden System als Zeilenbegrenzer
verwendet wird, kann mit System.getProperties() abgefragt werden,
siehe Programm SystemProperty (3.3),
z.B. CR LF,
d.h. die Zeichenfolge mit den ASCII-Code-Nummern 13 10.
Im folgenden Programm KfzReparatur werden Rechnungen für Kraftfahrzeug-Repararen erstellt durch Eingabe der Nummern der betreffenden Positionen für Arbeitswerte und Materialwerte.
Das Programm arbeitet mit zwei bereits fertig editierten Text
-Dateien ArbWert.txt bzw. MatWert.txt , in denen, abgesehen von
noch zu berücksichtigenden aktuellen Kosten-Faktoren 9.5 und 4.5,
zeilenweise die Kosten für die jeweilige Arbeits- bzw. MaterialPosition ausgewiesen sind.
In der Methode sumUp(String,int,double) wird die Position pos
vom Benutzer abgefragt und die Datei filnam solange zeilenweise
gelesen, bis in.getLineNumber() gleich pos ist.
//********************** KfzReparatur.java ***********************
// Kfz-Reparatur-Rechnungstellung nach Arb/MatWert-Positionen *
// Command: java KfzReparatur USt ArbWert.txt maxposAW facAW *
// MatWert.txt maxposMW facAW *
// ConsoleInput.java muss im aktuellen Verzeichnis stehen *
//****************************************************************
// java.lang. Object
import java.io .*; // FileReader'||||`IOException
// BufferedReader'|||
// LineNumberReader' |||
import static java.lang.System .*; // ||`System
import static java.lang.Double .parseDouble; // |`Double
import static java.lang.Integer.parseInt; // `Integer
class KfzReparatur extends ConsoleInput
{static double sumUp(final String filnam,int maxpos,double fac)
{double sum=0.0;
out.println (filnam);
try
{LineNumberReader in=
new LineNumberReader(new FileReader(filnam));
in.mark (maxpos*80); // mark position and note limit
for(;;)
{out.print ("pos(1-"+maxpos+", 0 break):");
int pos=getInt();
if (pos==0) {break;};
if (pos<0 || pos>maxpos) {continue;}
in.reset (); // reset to marked position
in.setLineNumber(1);
while (in.getLineNumber()<pos) {in.readLine();}
in.skip (3L) ;char[] buf= new char[11];
in.read(buf,0,11);String s= new String(buf) ;
in.read(buf,0,11);double val=parseDouble(new String(buf));
sum+=val*=fac;
out.printf (" %1$10s %2$6.2f\r\n",s,val);
}in.close ();
}
catch(IOException e){err.println(e.toString());}
out.println (" ------");
out.printf ("Summe EUR %6.2f\r\n",sum);
out.println (" ------");
return sum;
}
public static void main(String[] args)//args[0]..args[6] genutzt
{final double
TOTAL=sumUp(args[1],parseInt(args[2]),parseDouble(args[3]))+
sumUp(args[4],parseInt(args[5]),parseDouble(args[6])),
USt = TOTAL*0.01* parseDouble(args[0]);
out.printf ("Total EUR %6.2f\r\n",TOTAL );
out.printf ("USt EUR %6.2f\r\n", USt);
out.println (" ------");
out.printf ("Gesamt EUR %6.2f\r\n",TOTAL+USt);
} }
//****************************************************************
Command
----------------------------------------------------------
java KfzReparatur 19.0 ArbWert.txt 3 9.5 MatWert.txt 3 4.5
ArbWert.txt
MatWert.txt
-------------------------
-------------------------
1 ReinigsArb. 2.00
1 ZierLeiste 5.10
2 LackierArb. 3.25
2 Scheibe 7.50
3 ScheibMont. 14.50
3 ScheibRahm. 10.25
Output
Input
Output (Fortsetzung)
Input
-------------------------
-----
-------------------------
-----
ArbWert.txt
MatWert.txt
pos(1-3, 0 break):
1
pos(1-3, 0 break):
2
ReinigsArb. 19,00
Scheibe 33,75
pos(1-3, 0 break):
3
pos(1-3, 0 break):
3
ScheibMont. 137,75
ScheibRahm. 46,13
pos(1-3, 0 break):
2
pos(1-3, 0 break):
1
LackierArb. 30,88
ZierLeiste 22,95
pos(1-3, 0 break):
0
pos(1-3, 0 break):
0
------
------
Summe EUR 187,63
Summe EUR 102,83
------
------
Total EUR 290,45
USt EUR 55,19
------
Gesamt EUR 345,64
ConsoleInput ist die in 8.2 beschriebene Klasse mit der Methode
getInt() zur Keyboard-Eingabe eines int-Werts über die DOS-Console. Die Klasse ConsoleInput.java und die Text - Dateien
ArbWert.txt, MatWert.txt müssen im aktuellen Verzeichnis stehen.
Die Text-Zeilen der Text-Dateien ArbWert.txt und MatWert.txt
betehen aus der 3 byte breiten Positionsnummer, der 11 byte breiten Bezeichnung und dem 11 byte breiten Preis.
Die Methode sumUp(String,int,double) hat als formalen String-Parameter den Namen filnam einer externen Text-Datei und öffnet mit
LineNumberReader in=new LineNumberReader(new FileReader(filnam));
eine lokal in sumUp vereinbarte interne Text-Datei in als Zeiger
auf die externe Text-Datei filnam. Da diese nur gelesen werden
soll, werden "Reader"-Klassen (Fig. 8) gewählt. Die mit readLine()
jeweils eingelesene Zeile wird mit read(buf,0,11) gepuffert eingelesen auf einen 11-Zeichen-String buf, der schließlich in den
gesuchten double Wert val konvertiert wird.
Wenn nicht mehr aus der externen Datei filnam gelesen werden
soll, sollte man dies mit in.close() absichern, d.h. das "Buch
filnam" wird für den "Leser in" geschlossen.
Das Hauptprogramm ruft sumUp(String,int,double) für die Dateien
ArbWert.txt und MatWert.txt auf, ist aber selbst von Dateien frei.
"Stream"-Klassen (Fig. 8) ermöglichen die Verarbeitung von BinärDateien. Direkte Verarbeitung mit Editoren ist i.a. nicht möglich.
-----------------------
VVVVVVVVVVVVVVV
Binär-Datei (Prinzip)
VVVVVVVVVVVVVVV
VV
-----------------------
VV
VV VV
VV Binär-Dateien können u.a. aus VV
VV nicht-editierbaren (Kontroll-) Zeichen bestehen VV
VV und enthalten i.a. keine Zeilenbegrenzer. VV
VV Die Länge einer Binär-Datei ist abfragbar. VV
VV VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Jede Text-Datei ist auch als Binär-Datei deutbar und kann daher
mit "Stream"-Klassen verarbeitet werden. Umgekehrt kann nicht jede
Binär-Datei als Text-Datei gedeutet und editiert oder mit "LineNumber"-Klassen verarbeitet werden.
Im folgenden Beispiel Type wird das bekannte DOS-Kommando TYPE
verallgemeinert auf Binär-Dateien. Das Kommando
java Type BinFile TextFile
wird ähnlich wie TYPE TextFile mit dem Namen einer (Text- oder)
Binär-Datei als Argument args[0] aufgerufen und gibt diese Datei
als Text-Datei auf dem Argument args[1] aus. Nicht mit eigenem
Symbol druckbare (Kontroll-) Zeichen werden mit Fluchtsymbol `
und Anfangsbuchstaben ihres ASCII-Bezeichners notiert, außer A für
BEL, O für SO und I für SI.
Zeilenvorschub wird nach Zeilen-Begrenzer `C`L und nach je 78
Zeichen gegeben. Das Erreichen des Endes der Binär-Datei wird abgefragt mit
Im Beispiel wird als args[0] nicht wirklich eine Binär-Datei,
sondern speziell die eigene Text-Datei Type.java und als args[1]
die DOS-Console con gewählt:
d.h. Type gibt das Programm Type.java auf Bildschirm aus mit Angabe der sonst nicht sichtbaren Zeilen-Begrenzer `C für CarriageReturn und `L für LineFeed und Ausführung des Zeilenvorschubs.
//************************** Type.java ***************************
// Erweiterung des DOS-Befehls Type auf Binaer-Dateien. *
// Nicht mit eigenem Symbol druckbare (control) Zeichen *
// werden mit Fluchtsymbol ` und Anfangsbuchstaben des *
// ASCII-Bezeichners notiert, ausgenommen A,O,I: A steht *
// fuer BEL (ALARM), O steht fuer SO, I steht fuer SI. *
// Gibt Zeilenvorschub nach `C`L und nach je 78 Zeichen. *
// Command: java Type BinOrTxtFile TxtFile *
//****************************************************************
// java.lang. Object
import java.io.*;//FileInputStream'||||`FileOutputStream
// |||`Writer
// InputStreamReader'|| `OutputStreamWriter
// |`IOException
import static java.lang.System.err; // `System
class Type
{public static void main(String[] args)//args[0]..args[1] genutzt
{try
{InputStreamReader in=new InputStreamReader
(new FileInputStream (args[0]));
OutputStreamWriter out=new OutputStreamWriter
(new FileOutputStream(args[1]));
int i,i0=0, clm =0;
while ((i= in.read())!=-1) // InputStreamReader
{switch(i) // Writer
{case 0 : out.write("`N" );clm+=2; break; // NUL
case 7 : out.write("`A" );clm+=2; break; // BEL
case 8 : out.write("`B" );clm+=2; break; // BS
case 9 : out.write("`H" );clm+=2; break; // HT
case 10 : out.write("`L" );clm+=2; // LF
if(i0==13) {out.write("\r\n");clm =0;}break;
case 11 : out.write("`V" );clm+=2; break; // VT
case 12 : out.write("`F" );clm+=2; break; // FF
case 13 : out.write("`C" );clm+=2; break; // CR
case 14 : out.write("`O" );clm+=2; break; // SO
case 15 : out.write("`I" );clm+=2; break; // SI
case 26 : out.write("`S" );clm+=2; break; // SUB
case 27 : out.write("`E" );clm+=2; break; // ESC
case 96 : out.write("``" );clm+=2; break; // `
case 127: out.write("`D" );clm+=2; break; // DEL
case 255: out.write("`R" );clm+=2; break; // RES
default : out.write( i );clm++ ; // Outp.Str.Wr.
} i0=i;
if (clm>77) {out.write("\r\n");clm =0;} // Writer
} out.close(); // OutputStreamWriter
in.close(); // InputStreamReader
}
catch(IOException e){err.println(e.toString());}
}
}
//****************************************************************
Command (Output on Console)
----------------------------
java Type Type.java con
Output (gekuerzt)
-----------------------------------------------------
... class Type`C`L
{public static void main(String[] args) ... `C`L
... }`C`L ...
Die folgende Klasse ConsoleInput stellt Methoden get...() bereit
zur Keyboard-Eingabe von String- und Grundtyp-Werten über die DOS
-Console con, siehe z.B. das Programm KfzReparatur (8.1).
Für String-Eingabe verwendet man die Methode get(), für Grundtyp-Eingaben muss der Grundtyp im Methoden-Bezeichner genannt werden, z.B. getDouble() für Double-Eingabe. Damit kann entsprechend
dem Methoden-Bezeichner der richtigen Parser gewählt werden, z.B.
der Parser Double.parseDouble() für die Methode getDouble() .
//*********************** ConsoleInput.java **********************
// Methoden get...() als Kurzschreibweise *
// fuer Konsole-Eingabe in.read() und Parsen parse...() *
//****************************************************************
// java.lang. O b j e c t
import static java.lang.System .*; // |||||||`System
import static java.lang.Boolean.parseBoolean; // ||||||`Boolean
import static java.lang.Byte .parseByte; // |||||`Byte
import static java.lang.Short .parseShort; // ||||`Short
import static java.lang.Integer.parseInt; // |||`Integer
import static java.lang.Long .parseLong; // ||`Long
import static java.lang.Float .parseFloat; // |`Float
import static java.lang.Double .parseDouble; // `Double
class ConsoleInput
{
static String get()
{try
{byte[] buffer=new byte[80]; // max. 1 Zeile Eingabe
in.read (buffer); return new String(buffer).trim();
}
catch(Exception e) {return e.toString();}
}
static char getChar() {return (get()+" ").charAt(0);}
static boolean getBoolean() {return parseBoolean (get());}
static byte getByte() {return parseByte (get());}
static short getShort() {return parseShort (get());}
static int getInt() {return parseInt (get());}
static long getLong() {return parseLong (get());}
static float getFloat() {return parseFloat (get());}
static double getDouble() {return parseDouble (get());}
}
//****************************************************************
Der Aufruf der Methoden get...() der Kasse ConsoleInput wird
vorgeführt am Beispiel ConsoleInputDemo:
//******************** ConsoleInputDemo.java *********************
// Demonstration von ConsoleInput *
// ConsoleInput.java steht im aktuellen Verzeichnis *
//****************************************************************
// java.lang. Object
import static java.lang.System.out; // `System
class ConsoleInputDemo extends ConsoleInput
{public static void main(String[] args) // args ungenutzt
{out.print("String: " );out.println(get ());
out.print("char: " );out.println(getChar ());
out.print("boolean: ");out.println(getBoolean());
out.print("byte: " );out.println(getByte ());
out.print("short: " );out.println(getShort ());
out.print("int: " );out.println(getInt ());
out.print("long: " );out.println(getLong ());
out.print("float: " );out.println(getFloat ());
out.print("double: " );out.println(getDouble ());
}
}
//****************************************************************
Output
Input
----------------------
----------------------
String:
Kette aus Zeichen
Kette aus Zeichen
char:
c
c
boolean:
true
true
byte:
127
127
short:
32767
32767
int:
2147483647
2147483647
long:
9223372036854775807
9223372036854775807
float:
3.4028235E38
3.4028235E38
double:
1.7976931348623157E308
1.7976931348623157E308
zu Frage
abdeckbare Antwort
---------------------------------------------
--------------------
8 Aus Daten welchen Typs bestehen her-
byte
kömmlich Dateien in DOS?
8 Welcher Datentyp ist in Java für
char
Text-Dateien aus 2-byte Unicode -
Zeichen vorgesehen?
8.1 Wie bewirkt man Zeilenpositionierung
LineNumberReader:
bei Text-Dateien?
mark(),reset(),
setLineNumber(1),
getLineNumber()
--------------
8.1/2 Wie ist die Hierarchie,
Object
die von der Klasse
|
Object zu den Klassen
R e a d e r (abstract)
LineNumberReader
| |
(für Text-Dateien) und
BufferedReader InputStreamReader
InputStreamReader (für
|
Binär-Dateien) führt?
LineNumberReader
--------------
8.1/2 Welche Klasse in der obigen Hierar-
Reader (abstract)
chie definiert erstmals mark(int) zum
Markieren und reset() zum Zurücksetzen
auf die markierte Anfangsposition?
8.2 Warum kann man in Type nicht das zwei-
Die Umlenkung ">"
te Argument fest durch "con" ersetzen
funktioniert
und ggf. java BinFile > TxtFile wählen?
nicht für write().
8.2 Schreibe ein Programm Untype, das eine
Ähnlich wie Type,
mit Type erzeugte Text-Datei wieder in
im switch Umkehr-
die ursprüngliche Binär-Datei umformt.
Zeichen-Zuordnung