Posts mit dem Label I/O werden angezeigt. Alle Posts anzeigen
Posts mit dem Label I/O werden angezeigt. Alle Posts anzeigen

Donnerstag, 30. Juli 2009

File-I/O: RandomAccessFile

Ein Einfacher Weg, um ein File zu öffnen, um darin Werte zu lesen oder zu schreiben, ist ein RandomAccessFile.
die Klasse ist eigentlich selbsterklärend. Zusammengefasst tut sie folgendes:

Je nach dem, in welchem Modus eine Datei geöffnet wurde, kann man entweder Lese-, Schreibmethoden oder beides aufrufen. Für diese Operationen wird ein "filePointer" benutzt, der sozusagen als Schreib-/Lesekopf fungiert: Jede Operation führt den filePointer vorwärts an den bytes der Datei vorbei, und entweder werden die passierten bytes gelesen oder überschrieben.

Für die normalen Datentypen von Java ist das eine einfache Sache. Z.B. ist ein int eine 32-bit Zahl. readInt() und writeInt() bewegen also den filePointer jeweils um 4 bytes weiter. Bei Strings, und das ist der einzige andere Typ, den man mit RandomAccessFile bearbeiten kann, ist die Sache komplizierter. Strings haben keine fixe Länge, und für sie gibt es auch mehr Methoden:
readUTF()
writeUTF(String)
Diese Methoden sind ein echtes Paar: etwas, das man mit writeUTF geschrieben hat, kann man mit readUTF wieder eins zu eins auslesen. Aber:
readLine()
writeBytes(String)
writeChars(String)
Hier wird es ein Bisschen unübersichtlich. writeChars passt eigentlich gar nicht rein, weil Zeichen damit mit 2 bytes gespeichert werden, writeBytes (klarerweise) und auch readLine benutzen ein byte.
Die beiden Schreibmethoden können dabei beliebig viele Zeilen schreiben, weil ein String ja Zeilenumbrüche enthalten kann. außerdem wird das Geschriebene nicht mit einem Zeilenumbruch aufhören, wenn man nicht dafür sorgt. readLine dagegen wird auf leden Fall genau eine ganze Zeile lesen.
Insgesamt gesehen ist writeChars eigentlich fast nicht zu gebrauchen. writeBytes und readLine sollte man nur verwenden, wenn die Datei nur Text enthält. Ansonsten macht man sich damit nur Probleme.

Donnerstag, 16. Juli 2009

File-I/O: Die File-Klasse

Wenn man ein Programm schreibt, kommt man oft in die Situation, dass man Daten oder Einstellungen speichern möchte, damit sie beim nächsten Programmstart wieder verfügbar sind. Der einfachste Weg ist, diese Daten in einer Datei zu speichern.

Dazu sollte man aber wissen, wie in Java Pfadangaben funktionieren, also testen wir das kurz (ich arbeite auf Linux, also schauen meine Ausgaben anders aus als unter Windows):

package post07;


import java.io.File;
import java.io.IOException;


public class FileTest {
public static void main(String[] args) {
//Eine relative Pfadangabe. Unter Linux ist das eine Angabe, die nicht mit "/" beginnt.
//Dieser Pfad ist relativ zum Projektverzeichnis
File f1 = new File("relativ");
//Hier kommt "relativ" zurück, also der Pfad, den man oben angegeben hat
System.out.println(f1.getPath());
//Hier kommt der absolute Pfad zurück. In meinem Fall ist das
//"/home/clemens/workspaces/SE/ProggersDiary/relativ"
//Unter Windows würde das mit dem Laufwerksbuchstaben beginnen, und die einzelnen Ordner würden mit "\"
//getrennt.
System.out.println(f1.getAbsolutePath());

//Der erste Teil ist der Ordner, zu dem die zweite angabe relativ sein soll
//System.getProperty("user.home") liefert dabei das Home-Directory ("/home/clemens" bei mir)
File f2 = new File(System.getProperty("user.home"), "relativ");
//Der Pfad und der absolute Pfad sind in diesem Fall gleich, weil die erste Angabe absolut war
System.out.println(f2.getPath());
System.out.println(f2.getAbsolutePath());

//Hier benutze ich ".." um mich in den Ordner "/home" zu bewegen
File f3 = new File(System.getProperty("user.home"), "../relativ");
//Hier bemerkt man das aber noch nicht. beide Pfade sind "/home/clemens/../relativ"
System.out.println(f3.getPath());
System.out.println(f3.getAbsolutePath());
try {
//Im canonical path wird das dann eliminiert, außerdem werden symlinks azfgelöst. Da diese Operation
//Zugriff auf das Filesystem benötigt, braucht man hier einen try-catch-block
//das ergebnis ist "/home/relativ"
System.out.println(f3.getCanonicalPath());
} catch(IOException ex) {}
}
}
Das heißt also:
  • Relative Angaben sind normalerweise relativ zum Projektverzeichnis (Wenn das Programm in ein jar-File gepackt ist, ist das wieder nicht möglich, also ist diese Variante stark von der Laufzeitumgebung abhängig!)
  • Alternativ kann man einen Pfad Angeben, auf der die relative angabe basiert. Diese Angabe kann sowohl ein String (wie im Beispielcode) als auch ein File-Objekt sein
  • Natürlich sind auch direkt absolute Angaben möglich
Was man jetzt hier nicht direkt gesehen hat ist, dass ei File-Objekt nicht unbedingt einer Datei oder einem Ordner im Dateisystem entsprechen muss. Um das herauszufinden, benötigt man
exists()
isFile()
isDirectory()

Wenn ein File nicht existiert, kann es erzeugt werden (wobei natürlich eine Exception auftreten kann):
createNewFile()
mkdir()
mkdirs()

Eine Entsprechung von mkdirs für files gibt es nicht, aber sie kann einfach erzeugt werden:
try {
//Woher auch immer das File kommt...
File f = ...;
//Der Ordner, der die Datei enthalten soll, wird mit mkdirs erzeugt
f.getParentFile().mkdirs();
//Jetzt existiert der Ordner garantiert, also kann man createNewFile benutzen
f.createNewFile();
} catch(IOException ex) {
//Falls irgendwo was schief gegangen ist, sieht man es hier
ex.printStackTrace();
}
So, das nächste mal werden wir wirklich in Dateien schreiben ;)