Tit   Inh   Ind   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   <<   <   >   >> 

13.4 Random-Access-Dateien



Neben der stream-basierten Ein- und Ausgabe bietet die Java-Klassenbibliothek auch die Möglichkeit des wahlfreien Zugriffs auf Dateien. Dabei wird eine Datei nicht als sequentielle Folge von Bytes angesehen, sondern als eine Art externes Array, das an beliebiger Stelle gelesen oder beschrieben werden kann.

Für den Zugriff auf Random-Access-Dateien stellt das Paket java.io die Klasse RandomAccessFile zur Verfügung. Anders als bei der stream-orientierten Ein-/Ausgabe kann dabei allerdings nur auf Dateien zugegriffen werden; die Verwendung von Arrays oder Pipes als Dateiersatz wird nicht unterstützt. Auch das durch die Streams realisierte Filterkonzept ist in Random-Access-Dateien nicht zu finden.

Die Klasse RandomAccessFile stellt Methoden zum Anlegen neuer Dateien und zum Öffnen vorhandener Dateien zur Verfügung. Für den lesenden oder schreibenden Zugriff auf die Datei gibt es ähnliche Methoden wie bei der sequentiellen Dateiverarbeitung. Zusätzlich kann der Satzzeiger wahlfrei positioniert werden, und es ist möglich, seine aktuelle Position abzufragen.

13.4.1 Öffnen, Neuanlegen und Schließen

Das Öffnen von Random-Access-Dateien erfolgt mit dem Konstruktor der Klasse RandomAccessFile, der in zwei verschiedenen Varianten zur Verfügung steht:

public RandomAccessFile(File file, String mode)
  throws IOException

public RandomAccessFile(String name, String mode)
  throws FileNotFoundException
java.io.RandomAccessFile

Bei der Übergabe des String-Parameters name wird die Datei mit diesem Namen geöffnet und steht für nachfolgende Schreib- und Lesezugriffe zur Verfügung. Bei der Übergabe eines File-Objekts wird die durch dieses Objekt spezifizierte Datei geöffnet. Der zweite Parameter mode gibt die Art des Zugriffs auf die Datei an. Er kann entweder "r" sein, um die Datei nur zum Lesen zu öffnen, oder "rw", um sie zum Schreiben und Lesen zu öffnen. Ein reiner Schreibmodus, wie er beispielsweise unter UNIX möglich wäre, wird nicht unterstützt.

Während der Testphase des JDK 1.2 wurde die Deklaration der Konstruktoren teilweise dahingehend geändert, daß bei Zugriffsproblemen nicht mehr die Ausnahme IOException, sondern FileNotFoundException ausgelöst wird. Gründe für eine der beiden Ausnahmen können sein, daß eine nicht vorhandene Datei zum Lesen geöffnet werden soll, daß der Dateiname ein Verzeichnis bezeichnet oder daß keine ausreichenden Zugriffsrechte auf die Datei zur Verfügung stehen.

 JDK1.1/1.2 

Leider gibt es in der Klasse RandomAccessFile keine explizite Differenzierung zwischen dem Öffnen einer Datei zum Schreiben und dem Neuanlegen. Hier gilt die implizite Regel, daß eine Datei neu angelegt wird, wenn sie beim Öffnen im Modus "w" nicht vorhanden ist. Existiert sie dagegen bereits, wird sie unverändert geöffnet, und es gibt keine Möglichkeit, ihren Inhalt zu löschen oder die Dateilänge auf einen bestimmten Wert zu setzen. Glücklicherweise bietet die Klasse File mit der Methode delete die Möglichkeit, eine Datei zu löschen und so das Neuanlegen über einen kleinen Umweg doch zu erreichen.

 Warnung 

Das Schließen einer Random-Access-Datei erfolgt wie bei Streams durch Aufruf der parameterlosen Methode close. Diese leert zunächst alle internen Puffer und ruft dann die korrespondierende Betriebssystemfunktion zum Schließen der Datei auf.

13.4.2 Positionierung des Dateizeigers

Einer der Hauptunterschiede zwischen dem sequentiellen und dem wahlfreien Zugriff auf eine Datei besteht darin, daß beim wahlfreien Zugriff mit einem expliziten Satzzeiger gearbeitet wird. Jeder Schreib- und Lesezugriff erfolgt dabei an der Position, die durch den aktuellen Inhalt des Satzzeigers bestimmt wird, und positioniert den Zeiger um die Anzahl gelesener bzw. geschriebener Bytes weiter. Die Klasse RandomAccessFile stellt eine Reihe von Methoden zum Zugriff auf den Satzzeiger zur Verfügung:

public long getFilePointer()

public void seek(long pos)

public void skipBytes(int n)

public long length()
java.io.RandomAccessFile

getFilePointer liefert die aktuelle Position des Satzzeigers; das erste Byte einer Datei steht dabei an Position 0. Da der Rückgabewert vom Typ long ist, unterstützt Java den Zugriff auf Dateien, die größer als 2 GByte sind, sofern es das Betriebssystem zuläßt.

Das Positionieren des Satzzeigers erfolgt mit der Methode seek, die den Satzzeiger an die durch pos angegebene Stelle positioniert. Anders als in C bezieht sich der Wert von pos dabei immer auf den Anfang der Datei. Das Positionieren relativ zur aktuellen Position kann mit der Methode skipBytes erledigt werden. Die neue Position wird dabei aus der aktuellen Position plus dem Inhalt des Parameters n berechnet. Auch negative Werte für n sind dabei erlaubt und bewirken eine Rückwärtsverschiebung des Satzzeigers.

13.4.3 Lesezugriffe

Für die lesenden Zugriffe auf eine Random-Access-Datei stehen die folgenden Methoden zur Verfügung:

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

Sie lesen jeweils ein Element des angegeben 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 ganze Zeile Text aus der Eingabedatei und gibt sie als String an den Aufrufer zurück.

Darüber hinaus steht auch eine Reihe von read-Methoden zur Verfügung, die zum Einlesen eines einzelnen Bytes oder einer Menge von Bytes verwendet werden können:

public int read()
public int read(byte b[])
public int read(byte b[], int off, int len)
java.io.RandomAccessFile

13.4.4 Schreibzugriffe

Die schreibenden Zugriffe erfolgen mit analogen Methoden:

public final void writeBoolean(boolean v)
public final void writeByte(int v)
public final void writeBytes(String s)
public final void writeChar(int v)
public final void writeChars(String s)
public final void writeDouble(double v)
public final void writeFloat(float v)
public final void writeInt(int v)
public final void writeLong(long v)
public final void writeShort(int v)
public final void writeUTF(String str)
java.io.RandomAccessFile

Zusätzlich gibt es auch hier einige elementare write-Methoden :

public void write(int b)
public void write(byte b[])
public void write(byte b[], int off, int len)
java.io.RandomAccessFile

Das folgende Listing zeigt die Verwendung der Klasse RandomAccessFile am Beispiel eines Programms, das die Signatur und Versionsnummer aus einem .class-File herausliest. Die Signatur einer Klassendatei ergibt das Wort »CAFEBABE«, wenn man die hexadezimale Darstellung der ersten vier Bytes ausgibt. In den nächsten beiden Bytes folgt die Minor-Versionsnummer und in den darauffolgenden zwei Bytes die Major-Versionsnummer.

Das nachfolgende Programm implementiert eine Klasse ClassFileReader, die den Zugriff auf die Klassendatei ermöglicht. Der Konstruktor öffnet die Datei, und die Ausgabe der Signatur und Versionsinformation erfolgt mit Hilfe der Methoden printSignature und printVersion. Das Einlesen der Signatur erfolgt durch Lesen der ersten 4 Bytes der Datei, die dann jeweils in High- und Lowbyte zerlegt und in ihre hexadezimale Darstellung umgewandelt werden. Bei der Verwendung der Methode read zum Einlesen der Bytes ist zu beachten, daß der Rückgabewert vom Typ int ist. Er darf auch nicht in ein byte konvertiert werden, weil es sonst einen Vorzeichenüberlauf geben würde. Das Einlesen der Versionsnummern erfolgt mit der Methode readShort, die einen vorzeichenlosen 16-Bit-Wert aus der Datei liest. Auch hier ist der Rückgabewert vom Typ int, um den gesamten Wertebereich von 0 bis 65535 darstellen zu können.

001 /* Listing1310.java */
002 
003 import java.io.*;
004 
005 class ClassFileReader
006 {
007    private RandomAccessFile f;
008 
009    public ClassFileReader(String name)
010    throws IOException
011    {
012       if (!name.endsWith(".class")) {
013          name += ".class";
014       }
015       f = new RandomAccessFile(name,"r");
016    }
017 
018    public void close()
019    {
020       if (f != null) {
021          try {
022             f.close();
023          } catch (IOException e) {
024             //nichts
025          }
026       }
027    }
028 
029    public void printSignature()
030    throws IOException
031    {
032       String ret = "";
033       int b;
034 
035       f.seek(0);
036       for (int i=0; i<4; ++i) {
037          b = f.read();
038          ret += (char)(b/16+'A'-10);
039          ret += (char)(b%16+'A'-10);
040       }
041       System.out.println(
042          "Signatur...... "+
043          ret
044       );
045    }
046 
047    public void printVersion()
048    throws IOException
049    {
050       int minor, major;
051 
052       f.seek(4);
053       minor = f.readShort();
054       major = f.readShort();
055       System.out.println(
056          "Version....... "+
057          major+"."+minor
058       );
059    }
060 
061 }
062 
063 public class Listing1310
064 {
065    public static void main(String[] args)
066    {
067       ClassFileReader f;
068 
069       try {
070          f = new ClassFileReader("Listing1310");
071          f.printSignature();
072          f.printVersion();
073       } catch (IOException e) {
074          System.out.println(e.toString());
075       }
076    }
077 }
Listing1310.java
Listing 13.10: Lesen einer .class-Datei mit der Klasse RandomAccessFile

 Beispiel 

Die Ausgabe des Programms ist:

Signatur...... CAFEBABE
Version....... 45.3

 Tit   Inh   Ind   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   <<   <   >   >> 
Go To Java 2, Addison Wesley, Version 1.0.2, © 1999 Guido Krüger, http://www.gkrueger.com