Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 21 - Datei- und Verzeichnis-Handling

## 21.4 Zugriff auf Verzeichniseinträge

- 21.4 Zugriff auf Verzeichniseinträge
  - 21.4.1 Lesen von Verzeichniseinträgen
  - 21.4.2 Ändern von Verzeichniseinträgen

### 21.4.1 Lesen von Verzeichniseinträgen

Wurde ein `File`-Objekt für ein Verzeichnis konstruiert, so stehen weitere Methoden zur Verfügung, um auf die zusätzlichen Funktionen eines Verzeichnisses zuzugreifen. Mit Hilfe der Methode `list` ist es beispielsweise möglich, den Inhalt des Verzeichnisses auszulesen:

    public String[] list()
| java.io.File

`list` liefert ein Array von Strings, das für jeden gefundenen Verzeichniseintrag ein Element enthält. Die Liste enthält die Namen aller Dateien und Unterverzeichnisse mit Ausnahme von . und ... `list` gibt es noch in einer zweiten Variante, bei der die Auswahl der Verzeichniseinträge eingeschränkt werden kann. Dabei muß ein Objekt übergeben werden, das das Interface `FilenameFilter` implementiert. Dieses besitzt eine Methode `accept`, die für jede gefundene Datei aufgerufen wird und entscheidet, ob sie in die Liste aufgenommen werden soll oder nicht.

Zusätzlich gibt es die statische Methode `listRoots`, mit der eine Liste aller »Wurzeln« der verfügbaren Dateisysteme beschafft werden kann. Unter UNIX gibt es lediglich die Wurzel "/", unter Windows dagegen eine für jeden Laufwerksbuchstaben.

Eine häufig benötigte Funktion besteht darin, ein Verzeichnis samt seiner Unterverzeichnisse rekursiv zu durchlaufen und bei jedem gefundenen Eintrag eine bestimmte Aufgabe zu erledigen. Wir wollen zeigen, wie mit Hilfe des Visitor-Patterns (siehe Abschnitt 10.3.8) eine universell verwendbare Lösung geschaffen werden kann. Zunächst definieren wir dazu ein Interface `DirectoryVisitor`:

    001 /* DirectoryVisitor.java */
    002
    003 import java.io.*;
    004
    005 public interface DirectoryVisitor
    006 {
    007   public void enterDirectory(File dir);
    008   public void leaveDirectory(File dir);
    009   public void visitFile(File file);
    010 }
| DirectoryVisitor.java

Listing 21.2: Das Interface DirectoryVisitor

Beim Betreten eines Verzeichnisses wird `enterDirectory` aufgerufen, beim Verlassen `leaveDirectory`. Zu jedem darin enthaltenen Dateieintrag wird `visitFile` aufgerufen. Um es der konkreten Visitor-Implementierung so einfach wie möglich zu machen, werden sowohl Verzeichnisse als auch Dateien als fertige `File`-Objekte übergeben.

Ein konkreter Visitor, der eine Verzeichnisstruktur mit den darin enthaltenen Dateien und Unterverzeichnissen korrekt eingerückt auf dem Bildschirm ausgibt, entsteht durch Implementieren von `DirectoryVisitor` wie folgt:

    001 /* DirectoryPrintVisitor.java */
    002
    003 import java.io.*;
    004
    005 public class DirectoryPrintVisitor
    006 implements DirectoryVisitor
    007 {
    008   String indent = "";
    009
    010   public void enterDirectory(File dir)
    011   {
    012     System.out.println(indent + "[" + dir.getName() + "]");
    013     indent += "  ";
    014   }
    015
    016   public void leaveDirectory(File dir)
    017   {
    018     indent = indent.substring(2);
    019   }
    020
    021   public void visitFile(File file)
    022   {
    023     System.out.println(indent + file.getName());
    024   }
    025 }
| DirectoryPrintVisitor.java

Listing 21.3: Ein DirectoryVisitor zum Ausdrucken eines Verzeichnisses

In ähnlicher Weise kann ein `DirectoryVisitor` geschrieben werden, der die Anzahl der Dateien und Verzeichnisse zählt und die kumulierte Größe der darin enthaltenen Dateien ermittelt:

    001 /* DirectorySizeVisitor.java */
    002
    003 import java.io.*;
    004
    005 public class DirectorySizeVisitor
    006 implements DirectoryVisitor
    007 {
    008   int  files = 0;
    009   int  dirs  = 0;
    010   long size  = 0;
    011
    012   public void enterDirectory(File dir)
    013   {
    014     ++dirs;
    015   }
    016
    017   public void leaveDirectory(File dir)
    018   {
    019   }
    020
    021   public void visitFile(File file)
    022   {
    023     ++files;
    024     size += file.length();
    025   }
    026
    027   public int getDirs()
    028   {
    029     return dirs;
    030   }
    031
    032   public int getFiles()
    033   {
    034     return files;
    035   }
    036
    037   public long getSize()
    038   {
    039     return size;
    040   }
    041 }
| DirectorySizeVisitor.java

Listing 21.4: Die Klasse DirectorySizeVisitor

Nun fehlen nur noch die generische Methode für den rekursiven Verzeichnisdurchlauf und ein Beispielprogramm, das die Anwendung der Klassen im Zusammenspiel zeigt:

    001 /* Listing2105.java */
    002
    003 import java.io.*;
    004
    005 public class Listing2105
    006 {
    007   static void traverse(File dir, DirectoryVisitor visitor)
    008   {
    009     if (!dir.isDirectory()) {
    010       throw new IllegalArgumentException(
    011         "not a directory: " + dir.getName()
    012       );
    013     }
    014     visitor.enterDirectory(dir);
    015     File[] entries = dir.listFiles(
    016       new FileFilter()
    017       {
    018         public boolean accept(File pathname)
    019         {
    020           return true;
    021         }
    022       }
    023     );
    024     for (int i = 0; i < entries.length; ++i) {
    025       if (entries[i].isDirectory()) {
    026         traverse(entries[i], visitor);
    027       } else {
    028         visitor.visitFile(entries[i]);
    029       }
    030     }
    031     visitor.leaveDirectory(dir);
    032   }
    033
    034   public static void main(String[] args)
    035   {
    036     File file = new File(args[0]);
    037     //Bildschirmausgabe der Struktur
    038     traverse(file, new DirectoryPrintVisitor());
    039     //Größen ermitteln
    040     DirectorySizeVisitor visitor = new DirectorySizeVisitor();
    041     traverse(file, visitor);
    042     System.out.println("directories: " + visitor.getDirs());
    043     System.out.println("files: "       + visitor.getFiles());
    044     System.out.println("size: "        + visitor.getSize());
    045   }
    046 }
| Listing2105.java

Listing 21.5: Rekursiver Durchlauf von Verzeichnissen

Die eigentliche Arbeit wird von `traverse` erledigt. Sie stellt zunächst sicher, daß das übergebene `File`-Objekt auch tatsächlich ein Verzeichnis darstellt, und nicht etwa eine Datei. Ist das der Fall, wird auf dem übergebenen Visitor `enterDirectory` aufgerufen und mit `listFiles` eine Liste aller im Verzeichnis enthaltenen Dateien und Unterverzeichnisse beschafft. Zu jedem Unterverzeichnis erfolgt ein rekursiver Aufruf von `traverse`, zu jeder Datei wird `visitFile` aufgerufen. Nachdem alle Einträge abgearbeitet sind, wird das dem Visitor durch Aufruf von `leaveDirectory` mitgeteilt.

|  |  |  | Die Methode `listFiles` gibt es seit der Version 1.2 im JDK. Muß für eine ältere JDK-Version entwickelt werden, kann alternativ `list` verwendet werden. Diese gibt allerdings kein `File`-, sondern ein `String`-Array zurück. |  |
JDK1.1-1.4

### 21.4.2 Ändern von Verzeichniseinträgen

Neben dem Zugriff auf die Verzeichniseinträge gibt es in der Klasse `File` auch Methoden, um Dateien oder Verzeichnisse zu löschen oder umzubenennen und um Verzeichnisse neu anzulegen:

    public boolean mkdir()
    public boolean mkdirs()

    public boolean renameTo(File dest)

    public boolean delete()
| java.io.File

Die Methode `delete` löscht die durch das `File`-Objekt bezeichnete Datei. Mit `renameTo` wird das `File`-Objekt in das als Parameter übergebene Objekt umbenannt. Durch Aufruf von `mkdir` wird das durch das `File`-Objekt spezifizierte Verzeichnis angelegt. Mit `mkdirs` werden sogar alle Vaterverzeichnisse automatisch angelegt, wenn sie noch nicht existieren. Alle Methoden geben `true` zurück, wenn sie ihre Aufgabe erfolgreich ausführen konnten. Andernfalls geben sie `false` zurück.

Das folgende Listing zeigt die Verwendung der Klasse `File` und den Aufruf verschiedener Methoden:

    001 /* TestFile.java */
    002
    003 import java.io.*;
    004 import java.util.*;
    005
    006 public class TestFile
    007 {
    008   public static void main(String[] args)
    009   {
    010     File fil = new File("TestFile.java");
    011     TestFile.printFileInfo(fil);
    012     fil = new File("..");
    013     TestFile.printFileInfo(fil);
    014   }
    015
    016   static void printFileInfo(File fil)
    017   {
    018     System.out.println("Name= "+fil.getName());
    019     System.out.println("Path= "+fil.getPath());
    020     System.out.println("AbsolutePath= "+fil.getAbsolutePath());
    021     System.out.println("Parent= "+fil.getParent());
    022     System.out.println("exists= "+fil.exists());
    023     System.out.println("canWrite= "+fil.canWrite());
    024     System.out.println("canRead= "+fil.canRead());
    025     System.out.println("isFile= "+fil.isFile());
    026     System.out.println("isDirectory= "+fil.isDirectory());
    027     if (fil.isDirectory()) {
    028       String[] fils = fil.list();
    029       for (int i=0; i<fils.length; ++i) {
    030         System.out.println("  "+fils[i]);
    031       }
    032     }
    033     System.out.println("isAbsolute= "+fil.isAbsolute());
    034     System.out.println(
    035       "lastModified= "+(new Date(fil.lastModified()))
    036     );
    037     System.out.println("length= "+fil.length());
    038     System.out.println("");
    039   }
    040 }
| TestFile.java

Listing 21.6: Verwendung der Klasse File

Ein Aufruf des Programms liefert folgende Ausgabe:

    Name= TestFile.java
    Path= TestFile.java
    AbsolutePath= C:\ARC\DOKU\java\examples\TestFile.java
    Parent= null
    exists= true
    canWrite= true
    canRead= true
    isFile= true
    isDirectory= false
    isAbsolute= false
    lastModified= Sun Jan 05 17:15:56  1997
    length= 1242

    Name= ..
    Path= ..
    AbsolutePath= C:\ARC\DOKU\java\examples\..
    Parent= null
    exists= true
    canWrite= true
    canRead= true
    isFile= false
    isDirectory= true
      makefile
      html.cfg
      ...
      jdbc.sgml
      tuning.sgml
      reflection.sgml
    isAbsolute= false
    lastModified= Wed Jul 22 16:55:32 GMT+02:00 1998
    length= 0

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de