Titel   Inhalt   Suchen   Index   API  Go To Java 2, Zweite Auflage, Handbuch der Java-Programmierung
 <<    <     >    >>  Kapitel 19 - Byte-Streams

19.3 Eingabe-Streams



19.3.1 Die Basisklasse InputStream

Basis der Eingabe-Streams ist die abstrakte Klasse InputStream. Sie stellt folgende Methoden zur Verfügung:

public abstract int read()
  throws IOException

public int read(byte[] b)
  throws IOException

public int read(byte[] b, int off, int len)
  throws IOException

public long skip(long n)
  throws IOException

public int available()
  throws IOException

public void close()
  throws IOException

public void mark(int readlimit)

public void reset()
  throws IOException

public boolean markSupported()
java.io.InputStream

Die read-Methoden dienen dazu, Bytes zu lesen. Sie können entweder einzelne Bytes lesen (die als int zurückgegeben werden, dessen obere 3 Byte leer sind) oder ihre Daten direkt in einen Bytearray-Puffer schreiben. Mit skip kann eine beliebige Anzahl Bytes übersprungen werden. available liefert die Anzahl an Bytes, die ohne Blockieren mindestens gelesen werden können (Vorsicht, manche Implementierungen geben hier nie einen Wert größer als 1 zurück). Mit close wird der Eingabe-Stream geschlossen.

Die Methode markSupported gibt Auskunft darüber, ob Markieren/Positionieren unterstützt wird. Ist das der Fall, kann mit mark die aktuelle Position im Eingabestrom markiert und später mit reset dorthin zurückgesprungen werden. Das Argument von mark gibt dabei die maximale Anzahl an Zeichen an, die der Eingabestrom sich merken soll.

19.3.2 Aus InputStream direkt abgeleitete Klassen

Aus InputStream sind einige weitere Klassen direkt abgeleitet. Wie bei den Character-Streams bestimmen sie im wesentlichen die Art bzw. die Quelle der Dateneingabe.

FileInputStream

Ein FileInputStream stellt einen Byte-Stream zum Lesen aus einer Datei zur Verfügung. Er besitzt einige zusätzliche Konstruktoren:

public FileInputStream(String name)
  throws FileNotFoundException

public FileInputStream(File file)
  throws FileNotFoundException

public FileInputStream(FileDescriptor fdObj)
java.io.FileInputStream

Um eine Datei zu öffnen, kann entweder ihr Name oder ein dafür konstruiertes File-Objekt verwendet werden (siehe Kapitel 21. Existiert die Datei nicht oder kann nicht darauf zugegriffen werden, löst der Konstruktor eine FileNotFoundException aus. Mit Hilfe des dritten Konstruktors kann ein InputStream zu einer bereits geöffneten Datei erstellt werden.

Das folgende Programm zeigt die Verwendung der Klassen FileInputStream und FileOutputStream zum Kopieren einer Datei (Vorsicht, eine bereits vorhandene Zieldatei wird ohne Rückfrage überschrieben).

001 /* FileCopy.java */
002 
003 import java.io.*;
004 
005 public class FileCopy
006 {
007   public static void main(String[] args)
008   {
009     if (args.length != 2) {
010       System.out.println("java FileCopy inputfile outputfile");
011       System.exit(1);
012     }
013     try {
014       FileInputStream in = new FileInputStream(args[0]);
015       FileOutputStream out = new FileOutputStream(args[1]);
016       byte[] buf = new byte[4096];
017       int len;
018       while ((len = in.read(buf)) > 0) {
019         out.write(buf, 0, len);
020       }
021       out.close();
022       in.close();
023     } catch (IOException e) {
024       System.err.println(e.toString());
025     }
026   }
027 }
FileCopy.java
Listing 19.4: Kopieren einer Datei

ByteArrayInputStream

Die Klasse ByteArrayInputStream stellt einen Adapter dar, mit dessen Hilfe die Daten aus einem Byte-Array gelesen werden können. Sie besitzt zwei zusätzliche Konstruktoren, in denen die Datenquelle angegeben wird:

public ByteArrayInputStream(byte[] buf)
public ByteArrayInputStream(byte[] buf, int offset, int length)
java.io.ByteArrayInputStream

Im JDK gibt es eine Klasse StringBufferInputStream, die einen String als Datenquelle für einen Eingabe-Stream verwendet. Sie ist seit dem JDK 1.1 deprecated, weil die Konvertierung von UNICODE-Zeichen in Bytes nicht vollständig korrekt implementiert wurde. An ihrer Stelle ist die Klasse StringReader zu verwenden; sie wird in Abschnitt 18.3.2 erklärt.

 Hinweis 

ObjectInputStream

Ein ObjectInputStream erlaubt es, primitive Datentypen und Objekte von einem Input-Stream zu lesen. Zwar ist er nicht von FilterInputStream abgeleitet, wird aber ebenso verwendet und erwartet im Konstruktor einen InputStream als Datenquelle. Die Klasse ObjectInputStream ist eine der Säulen des Serialisierungs-APIs in Java und wird in Abschnitt 41.1.3 ausführlich beschrieben.

SequenceInputStream

Ein SequenceInputStream dient dazu, zwei oder mehr Eingabe-Streams so miteinander zu verbinden, daß die Daten nacheinander aus den einzelnen Streams gelesen werden. Die beteiligten Streams können entweder in einer Enumeration oder - wenn es sich um genau zwei von ihnen handelt - direkt an den Konstruktor übergeben werden:

public SequenceInputStream(Enumeration e)
public SequenceInputStream(InputStream s1, InputStream s2)
java.io.SequenceInputStream

PipedInputStream

Ein PipedInputStream ist das Gegenstück zum PipedOutputStream. Beide zusammen bilden eine Pipe zur Kommunikation zweier Threads. Ein Beispiel zur Anwendung der beiden Klassen findet sich in Abschnitt 22.4.4.

19.3.3 Aus FilterInputStream abgeleitete Klassen

FilterInputStream

Die aus InputStream abgeleitete Klasse FilterInputStream ist die Basisklasse aller gefilterten Eingabe-Streams. Diese definieren kein eigenes Eingabegerät, sondern bekommen es beim Instanzieren in Form eines InputStream-Arguments übergeben:

public FilterInputStream(inputStream in)
java.io.FilterInputStream

Die Aufgabe der aus FilterInputStream abgeleiteten Klassen besteht darin, die Lesezugriffe abzufangen, in einer für sie charakteristischen Weise zu verarbeiten und die Daten erst dann an den Aufrufer weiterzugeben.

BufferedInputStream

Ein BufferedInputStream dient zur Pufferung der Eingabedaten. Er kann insbesondere dann die Performance der Lesezugriffe erhöhen, wenn häufig nur kleine Datenmengen oder einzelne Bytes gelesen werden müssen. Ein BufferedInputStream besitzt zwei zusätzliche Konstruktoren, mit denen die Datenquelle und Puffergröße angegeben werden kann:

public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)
java.io.BufferedInputStream

PushbackInputStream

Die Klasse PushbackInputStream erweitert den Eingabe-Stream um die Fähigkeit, bereits gelesene Zeichen wieder zurückzunehmen. Dazu besitzt sie drei Methoden mit dem Namen unread, mit denen einzelne Bytes oder Byte-Arrays wieder zurückgegeben werden können:

public void unread(int b)
  throws IOException

public void unread(byte[] b, int off, int len)
  throws IOException

public void unread(byte[] b)
  throws IOException
java.io.PushbackInputStream

DataInputStream und DataInput

Analog zum DataOutputStream gibt es eine Klasse DataInputStream, mit der die von diesem geschriebenen Daten eingelesen werden können. Der DataInputStream implementiert das Interface DataInput, das folgende Methoden definiert:

void readFully(byte[] b) 
  throws IOException
void readFully(byte[] b, int off, int len) 
  throws IOException
int skipBytes(int n) 
  throws IOException
boolean readBoolean() 
  throws IOException
byte readByte() 
  throws IOException
int readUnsignedByte() 
  throws IOException
short readShort() 
  throws IOException
int readUnsignedShort() 
  throws IOException
char readChar() 
  throws IOException
int readInt() 
  throws IOException
long readLong() 
  throws IOException
float readFloat() 
  throws IOException
double readDouble() 
  throws IOException
String readLine() 
  throws IOException
String readUTF() 
  throws IOException
java.io.DataInput

Die einzelnen Methoden lesen jeweils ein Element des angegebenen Typs, das in dem durch die korrespondierende write...-Methode vorgegebenen binären Format vorliegen muß. readFully kann dazu verwendet werden, beliebig viele Datenbytes ungeachtet ihres Datentyps einzulesen. readLine liest eine Zeile Text aus der Eingabedatei und gibt sie als String an den Aufrufer zurück. Da die Eingabe byteweise gelesen wird, werden lediglich UNICODE-Zeichen von \u0000 bis \u00FF korrekt konvertiert. Die readUnsigned...-Methoden betrachten die Eingabe als vorzeichenlosen Wert im Bereich von 0 bis 255 (bzw. 0 bis 65535) und geben niemals negative Werte zurück.

Das folgende Programm liest die in Listing 19.2 erzeugten Daten ein und gibt sie auf der Console aus:

001 /* Listing1905.java */
002 
003 import java.io.*;
004 
005 public class Listing1905
006 {
007   public static void main(String[] args)
008   {
009     try {
010       DataInputStream in = new DataInputStream(
011                            new BufferedInputStream(
012                            new FileInputStream("test.txt")));
013       System.out.println(in.readInt());
014       System.out.println(in.readInt());
015       System.out.println(in.readDouble());
016       System.out.println(in.readUTF());
017       System.out.println(in.readUTF());
018       in.close();
019     } catch (IOException e) {
020       System.err.println(e.toString());
021     }
022   }
023 }
Listing1905.java
Listing 19.5: Verwendung der Klasse DataInputStream

CheckedInputStream

Die Klasse CheckedInputStream aus dem Paket java.util.zip dient dazu, die Prüfsumme zu einer Menge von Eingabedaten direkt beim Einlesen zu berechnen. Sie stellt einen Konstruktor zur Verfügung, mit dem das gewünschte Prüfsummenverfahren angegeben werden kann, und besitzt eine Methode getChecksum, mit der die Prüfsumme ermittelt werden kann:

public CheckedInputStream(InputStream in, Checksum cksum)

public Checksum getChecksum()
java.util.zip.CheckedInputStream

Das im Konstruktor erforderliche Objekt muß das Interface Checksum implementieren. Mit den beiden Klassen CRC32 und Adler32 stehen im JDK zwei vordefinierte Implementierungen zur Verfügung. Das folgende Programm berechnet die Adler-32-Prüfsumme zu der in der Kommandozeile angegebenen Datei:

001 /* Listing1906.java */
002 
003 import java.io.*;
004 import java.util.zip.*;
005 
006 public class Listing1906
007 {
008   public static void main(String[] args)
009   {
010     if (args.length != 1) {
011       System.out.println("Usage: java Listing1906 file");
012       System.exit(1);
013     }
014     try {
015       CheckedInputStream in = new CheckedInputStream(
016         new FileInputStream(args[0]),
017         new Adler32()
018       );                                             
019       byte[] buf = new byte[4096];
020       int len;
021       while ((len = in.read(buf)) > 0) {
022         //nichts 
023       }
024       System.out.println(in.getChecksum().getValue());
025       in.close();
026     } catch (IOException e) {
027       System.err.println(e.toString());
028     }
029   }
030 }
Listing1906.java
Listing 19.6: Berechnung der Adler-32-Prüfsumme

Entpacken von Dateien

Analog zur Klasse DeflaterOutputStream gibt es im Paket java.util.zip eine Klasse InflaterInputStream zum Entpacken von gepackten und/oder komprimierten Dateien. Die daraus abgeleiteten Klassen GZIPInputStream und ZipInputStream können direkt zum Entpacken von GZIP- und ZIP-Dateien verwendet werden.

Das folgende Programm zeigt, wie die mit dem Programm Zip.java aus Listing 19.3 gepackten Dateien wieder entpackt und dekomprimiert werden können:

001 /* Unzip.java */
002 
003 import java.io.*;
004 import java.util.zip.*;
005 
006 public class Unzip
007 {
008   public static void main(String[] args)
009   {
010     if (args.length != 1) {
011       System.out.println("Usage: java Unzip zipfile");
012       System.exit(1);
013     }
014     try {
015       byte[] buf = new byte[4096];
016       ZipInputStream in = new ZipInputStream(
017                           new FileInputStream(args[0]));
018       while (true) {
019         //Nächsten Eintrag lesen
020         ZipEntry entry = in.getNextEntry();
021         if (entry == null) {
022           break;
023         }
024         //Beschreibung ausgeben
025         System.out.println(
026           entry.getName() + 
027           " (" + entry.getCompressedSize() + "/" + 
028           entry.getSize() + ")"
029         );
030         //Ausgabedatei erzeugen
031         FileOutputStream out = new FileOutputStream(
032           entry.getName()
033         );
034         int len;
035         while ((len = in.read(buf)) > 0) {
036           out.write(buf, 0, len);
037         }
038         out.close();
039         //Eintrag schließen
040         in.closeEntry();
041       }
042       in.close();
043     } catch (IOException e) {
044       System.err.println(e.toString());
045     }
046   }
047 }
Unzip.java
Listing 19.7: Entpacken eines ZIP-Archivs

Das hier vorgestellte Programm dient - wie sein Gegenstück Zip.java - vor allem als "Proof of Concept". Einerseits bietet es nur einen Bruchteil der üblicherweise von einem Entpacker erwarteten Features (es werden beispielsweise keine Unterverzeichnisse automatisch erzeugt). Andererseits überschreibt es vorhandene Ausgabedateien ohne Vorwarnung. Der Umgang mit dem Programm sollte also mit der notwendigen Umsichtigkeit erfolgen.

 Warnung 


 Titel   Inhalt   Suchen   Index   API  Go To Java 2, Zweite Auflage, Addison Wesley, Version 2.0
 <<    <     >    >>  © 2000 Guido Krüger, http://www.gkrueger.com