Advanced Java Services | FileVisitor und die Methode Files.walkFileTree() |
Mit dem Interface FileVisitor<T> und der Methode walkFileTree() aus der Klasse Filesgibt es ab Java 7 eine einfache Möglichkeit ein Verzeichnis und dessen Inhalt rekursiv zu durchlaufen. Der Methode walkFileTree() übergibt man eine Implementierung des Interfaces und die Methode erledigt dann die Rekursion. Die Klasse SimpleFileVisitor<T> ist zudem eine einfache Realisierung des Interfaces FileVisitor<T> mit leeren Methoden.
Der FileVisitor vereinbart vier Methoden. ReturnTyp ist jeweils das Enum FileVisitResult.
Modifier and Type | Method and Description |
FileVisitResult | preVisitDirectory(T dir, BasicFileAttributes attrs)
Invoked for a directory before entries in the directory are visited. |
FileVisitResult | postVisitDirectory(T dir, IOException ex)
Invoked for a directory after entries in the directory, and all of their descendants, have been visited. |
FileVisitResult | visitFile(T file, BasicFileAttributes attrs)
Invoked for a file in a directory. |
FileVisitResult | visitFileFailed(T file, IOException ex)
Invoked for a file that could not be visited. |
Enum Constant | Description |
CONTINUE | Continue. |
SKIP_SIBLINGS | Continue without visiting the siblings of this file or directory. |
SKIP_SUBTREE | Continue without visiting the entries in this directory. |
TERMINATE | Terminate. |
Es folgen einige Beispiele.
Wir implementieren hier das Interface selbst.
import java.nio.file.Path; import java.io.IOException; import java.nio.file.FileVisitor; import java.nio.file.FileVisitResult; import java.nio.file.attribute.BasicFileAttributes; public class MyFileVisitor implements FileVisitor<Path> { private int fileCount = 0; private int dirCount = 0; /** * Hier ist Path immer ein Directory der Größe 0 */ @Override public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes bfa) throws IOException { System.out.println("preVisitDirectory: " + path + " size = " + bfa.size() + " bytes"); dirCount++; return FileVisitResult.CONTINUE; } /** * Hier ist Path immer ein Directory der Größe 0 * ex = null wenn keine Exception aufgetreten ist */ @Override public FileVisitResult postVisitDirectory(Path path, IOException ex) throws IOException { System.out.println("postVisitDirectory: " + path + " Exception = " + ex); return FileVisitResult.CONTINUE; } /** */ @Override public FileVisitResult visitFile(Path path, BasicFileAttributes bfa) throws IOException { System.out.println("visitFile: " + path + " size = " + bfa.size() + " bytes"); fileCount++; return FileVisitResult.CONTINUE; } /** */ @Override public FileVisitResult visitFileFailed(Path path, IOException ex) throws IOException { System.out.println("visitFileFailed " + " Exception = " + ex); return FileVisitResult.CONTINUE; } public int getFileCount() { return fileCount; } public int getDirCount() { return dirCount; } }
main kann etwa so aussehen.
import java.nio.file.Path; import java.nio.file.Paths; import java.io.IOException; import java.nio.file.Files; public class WalkFileTree_01 { public static void main(String[] args) { Path path = Paths.get("c:/tmp"); MyFileVisitor fileVisitor = new MyFileVisitor(); try { Files.walkFileTree(path, fileVisitor); System.out.print("found: " + fileVisitor.getFileCount() + " files in "); System.out.println(fileVisitor.getDirCount()+ " directories"); } catch(IOException ex) { ex.printStackTrace(); } } }
Eine mögliche Ausgabe
preVisitDirectory: c:\tmp size = 0 bytes visitFile: c:\tmp\datei.c size = 6 bytes visitFile: c:\tmp\datei.cpp size = 0 bytes visitFile: c:\tmp\datei.h size = 0 bytes visitFile: c:\tmp\datei.java size = 4 bytes ... preVisitDirectory: c:\tmp\java size = 0 bytes postVisitDirectory: c:\tmp\java Exception = null ... postVisitDirectory: c:\tmp Exception = null found: 57 files in 19 directories
In diesem Beispiel stützen wir uns auf den SimpleFileVisitor und überschreiben lediglich die notwendigen Methoden. Statt eines Konstruktors verfügt der SearchFileVisitor über eine statische Methode searchFor der man alle Parameter übergeben kann und die die gefundenen Dateien in einer Liste liefert. Dazu verwenden wir das Interface PathMatcher. Eion Objekt der implementierenden Klasse erhält man über das Defaultdateisystem.
public class SearchFileVisitor implements FileVisitor<Path>
{
private String searchPattern = "";
private List<Path> searchList;
/**
zulässige Pattern
* steht für eine Gruppe von Buchstaben, die für Dateinamen zulässig sind: "abc*.*"
? steht für einen Buchstaben, der für Dateinamen zulässig ist: "abc?fg?.j*"
*/
public static List<Path> searchFor(Path startDir, String searchPattern, boolean searchInFile) throws IOException
{
SearchFileVisitor fileVisitor = new SearchFileVisitor(searchPattern);
Files.walkFileTree(startDir, fileVisitor);
return fileVisitor.getResultList();
}
// private constructor
private SearchFileVisitor(String searchPattern)
{
this.searchPattern = searchPattern;
this.searchList = new ArrayList<>();
}
/**
*/
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes bfa) throws IOException
{
FileSystem fileSystem = FileSystems.getDefault();
PathMatcher pathMatcher = fileSystem.getPathMatcher("glob:" + searchPattern);
// Man muß zum Vergleich den reinen Dateinamen nehmen ohne Pfad !!!
if (pathMatcher.matches(path.getFileName()))
{
searchList.add(path);
}
return FileVisitResult.CONTINUE;
}
/**
*/
@Override
public FileVisitResult visitFileFailed(Path path, IOException ex) throws IOException
{
System.out.println("visitFileFailed " + " Exception = " + ex);
return FileVisitResult.CONTINUE;
}
public List<Path> getResultList()
{
return searchList;
}
}
Ein mögliches main
public static void main(String[] args) { try { Path startDir = Paths.get("c:/tmp"); String searchPattern = "*.{txt,java}";; boolean searchInFile = false; // Geht rekursiv durch alle Unterverzeichnisse // Rekursion wird in der Methode walkFileTree() gemacht List<Path> result = SearchFileVisitor.searchFor(startDir, searchPattern, searchInFile); for(Path path : result) { System.out.println(path + " size = " + Files.size(path) + " bytes" ); } } catch(IOException ex) { ex.printStackTrace(); } }
Eine mögliche Ausgabe
c:\tmp\datei.java size = 4 bytes c:\tmp\datei.txt size = 25 bytes c:\tmp\dokument.txt size = 11 bytes c:\tmp\nochnedatei.java size = 4 bytes c:\tmp\tempdir\dokument.txt size = 48 bytes