Java Programmierung
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                                  |
     |       ,-----------------------,                         |
     |       v                       v                         |                                                                                                        
     |  Datei-Zustand           Daten-Fluß                     |
     |    File...        ,-----------------------,             |
     |                   v                       v             |                                                                                                 
     |               Text-Datei             Binär-Datei        |
     |               LineNumber...          ...Stream...       |
     |             ,-----------,           ,-----------,       |
     |             v           v           v           v       |                                                                                                        
     |           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.




8.1      Text-Datei

  "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  |                       v                 v   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.

8.2      Binär-Datei

  "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
                      in.read() == -1

  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:
                      java Type Type.java con

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   |




8.3      Testfragen

 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
                                             |
    Java Programmierung
  Kap.09