След: Существенные Классы
Урок: Основной ввод-вывод
Раздел: Файловый ввод-вывод (Обладающий NIO.2)
Обход Дерева Файла
Домашняя страница > Существенные Классы > Основной ввод-вывод

Обход Дерева Файла

Вы должны создать приложение, которое рекурсивно посетит все файлы в дереве файла? Возможно, Вы должны удалить каждый .class файл в дереве, или находят каждый файл, к которому не получили доступ в прошлом году. Можно сделать так с FileVisitor интерфейс.

Этот раздел покрывает следующее:

Интерфейс FileVisitor

Чтобы обойти дерево файла, Вы сначала должны реализовать a FileVisitor. A FileVisitor определяет необходимое поведение в ключевых пунктах в процессе обхода: когда файл посещают, прежде, чем к каталогу получают доступ, после того, как к каталогу получают доступ, или когда отказ происходит. У интерфейса есть четыре метода, которые соответствуют этим ситуациям:

Если Вы не должны реализовать все четыре из FileVisitor методы, вместо того, чтобы реализовать FileVisitor интерфейс, можно расшириться SimpleFileVisitor class. Этот class, который реализует FileVisitor интерфейс, посещает все файлы в дереве и бросает IOError когда с ошибкой встречаются. Можно расширить этот class и переопределить только методы, которых Вы требуете.

Вот пример, который расширяется SimpleFileVisitor напечатать все записи в дереве файла. Это печатает запись, является ли запись регулярным файлом, символьной ссылкой, каталогом, или некоторым другим "неуказанным" типом файла. Это также печатает размер, в байтах, каждого файла. Любое исключение, с которым встречаются, печатается к консоли.

FileVisitor методы показывают полужирным:

import static java.nio.file.FileVisitResult.*;

public static class PrintFiles
    extends SimpleFileVisitor<Path> {

    // Print information about
    // each type of file.
    @Override
    public FileVisitResult visitFile(Path file,
                                   BasicFileAttributes attr) {
        if (attr.isSymbolicLink()) {
            System.out.format("Symbolic link: %s ", file);
        } else if (attr.isRegularFile()) {
            System.out.format("Regular file: %s ", file);
        } else {
            System.out.format("Other: %s ", file);
        }
        System.out.println("(" + attr.size() + "bytes)");
        return CONTINUE;
    }

    // Print each directory visited.
    @Override
    public FileVisitResult postVisitDirectory(Path dir,
                                          IOException exc) {
        System.out.format("Directory: %s%n", dir);
        return CONTINUE;
    }

    // If there is some error accessing
    // the file, let the user know.
    // If you don't override this method
    // and an error occurs, an IOException 
    // is thrown.
    @Override
    public FileVisitResult visitFileFailed(Path file,
                                       IOException exc) {
        System.err.println(exc);
        return CONTINUE;
    }
}

Заведение ножным стартером Процесса

Как только Вы реализовали Ваш FileVisitor, как Вы инициируете обход файла? Есть два walkFileTree методы в Files class.

Первый метод требует только начальной точки и экземпляра Вашего FileVisitor. Можно вызвать PrintFiles посетитель файла следующим образом:

Path startingDir = ...;
PrintFiles pf = new PrintFiles();
Files.walkFileTree(startingDir, pf);

Второе walkFileTree метод позволяет Вам дополнительно определить предел на числе уровней, которые посещают и ряд FileVisitOption перечисления. Если Вы хотите гарантировать, что этот метод обходит все дерево файла, можно определить Integer.MAX_VALUE для максимального параметра глубины.

Можно определить FileVisitOption перечисление, FOLLOW_LINKS, который указывает, что символьные ссылки должны сопровождаться.

Этот фрагмент кода показывает, как метод с четырьмя параметрами может быть вызван:

import static java.nio.file.FileVisitResult.*;

Path startingDir = ...;

EnumSet<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);

Finder finder = new Finder(pattern);
Files.walkFileTree(startingDir, opts, Integer.MAX_VALUE, finder);

Соображения, Создавая FileVisitor

Дерево файла является обойденной глубиной сначала, но невозможно заставить предположения об итерации упорядочить это, подкаталоги посещают.

Если Ваша программа будет изменять файловую систему, Вы должны тщательно рассмотреть, как Вы реализуете Ваш FileVisitor.

Например, если Вы пишете, что рекурсивное удаляет, Вы сначала удаляете файлы в каталоге прежде, чем удалить каталог непосредственно. В этом случае Вы удаляете каталог в postVisitDirectory.

Если Вы пишете рекурсивную копию, Вы создаете новый каталог в preVisitDirectory прежде, чем попытаться скопировать файлы в это (в visitFiles). Если Вы хотите сохранить атрибуты исходного каталога (подобный UNIX cp -p команда), Вы должны сделать это после того, как файлы были скопированы, в postVisitDirectory. Copy пример показывает, как сделать это.

Если Вы пишете поиск файла, Вы выполняете сравнение в visitFile метод. Этот метод находит все файлы, которые соответствуют Ваши критерии, но он не находит каталоги. Если Вы хотите найти и файлы и каталоги, следует также выполнить сравнение в любом preVisitDirectory или postVisitDirectory метод. Find пример показывает, как сделать это.

Вы должны решить, хотите ли Вы, чтобы символьные ссылки сопровождались. Если Вы удаляете файлы, например, после символьных ссылок не могло бы быть желательным. Если Вы копируете дерево файла, Вы могли бы хотеть позволить его. По умолчанию, walkFileTree не следует за символьными ссылками.

visitFile метод вызывается для файлов. Если Вы определили FOLLOW_LINKS у опции и Вашего дерева файла есть круговая ссылка к родительскому каталогу, о каталоге цикличного выполнения сообщают в visitFileFailed метод с FileSystemLoopException. Следующий фрагмент кода показывает, как поймать круговую ссылку и от Copy пример:

@Override
public FileVisitResult
    visitFileFailed(Path file,
        IOException exc) {
    if (exc instanceof FileSystemLoopException) {
        System.err.println("cycle detected: " + file);
    } else {
        System.err.format("Unable to copy:" + " %s: %s%n", file, exc);
    }
    return CONTINUE;
}

Этот случай может произойти только, когда программа следует за символьными ссылками.

Управление Потоком

Возможно, Вы хотите обойти поиск дерева файла определенного каталога и, когда найдено, Вы хотите, чтобы процесс завершился. Возможно, Вы хотите пропустить определенные каталоги.

FileVisitor методы возвращают a FileVisitResult значение. Можно прервать процесс обхода файла или управлять, посещают ли каталог значения, которые Вы возвращаете в FileVisitor методы:

В этом фрагменте кода называют любой каталог SCCS пропускается:

import static java.nio.file.FileVisitResult.*;

public FileVisitResult
     preVisitDirectory(Path dir,
         BasicFileAttributes attrs) {
    (if (dir.getFileName().toString().equals("SCCS")) {
         return SKIP_SUBTREE;
    }
    return CONTINUE;
}

В этом фрагменте кода, как только определенный файл располагается, имя файла печатается к стандартному выводу, и обход файла завершается:

import static java.nio.file.FileVisitResult.*;

// The file we are looking for.
Path lookingFor = ...;

public FileVisitResult
    visitFile(Path file,
        BasicFileAttributes attr) {
    if (file.getFileName().equals(lookingFor)) {
        System.out.println("Located file: " + file);
        return TERMINATE;
    }
    return CONTINUE;
}

Примеры

Следующие примеры демонстрируют механизм обхода файла:


Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Ссылки, Символьные или Иначе
Следующая страница: Обнаружение Файлов



Spec-Zone.ru - all specs in one place