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

21.4 Zugriff auf Verzeichniseinträge



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 muss 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.4.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, dass 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-6.0 

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, 5. Auflage, Addison Wesley, Version 5.0.2
 <<    <     >    >>   API  © 1998, 2007 Guido Krüger & Thomas Stark, http://www.javabuch.de