Статьи

Распечатка и фильтрация содержимого каталога в NIO.2

До выпуска Java 7 в области листинга содержимого каталогов не было особого интереса. Но поскольку в NIO.2 появился новый способ сделать это, возможно, стоит рассмотреть эту область. Одним из больших плюсов NIO.2 является возможность использовать распечатку и фильтрацию сразу в одном вызове метода. Это обеспечивает элегантное решение для большинства задач листинга / фильтрации, связанных с работой с файловой системой.

Список корневых каталогов

Если мы не работаем с относительными путями, нам нужно знать среду, в которой живет наше приложение, чтобы мы могли определять абсолютные пути. Поскольку файловые системы обычно представляют собой иерархические структуры, существует как минимум один корневой каталог. Чтобы правильно обращаться к файлам и каталогам, мы должны иметь возможность перечислить все эти корневые каталоги. Для этого мы обращаемся к самому экземпляру FileSystem , чтобы использовать его метод getRootDirectories , который является альтернативой Java 6-конструкции File.listRoots() .

1
2
3
Iterable<Path> it = FileSystems.getDefault().getRootDirectories();
 
System.out.println("Root file system locations: " + Sets.newHashSet(it));

* Обратите внимание, что класс Sets не является частью JDK, а поставляется из библиотеки Guava Google. Я использовал его здесь, просто для удобства, чтобы получить красиво отформатированное строковое представление корневых каталогов.

Со следующим выводом:

1
Root file system locations: C:\, D:\, E:\, F:\, G:\, H:\, I:\,

Распечатка и фильтрация содержимого каталога

Стандартной задачей при работе с файловой системой является просмотр или фильтрация файлов в данном каталоге. Нам может понадобиться изменить, проанализировать или просто перечислить их — по какой-то причине у класса java.nio.file.Files есть свои спины. Он предлагает три варианта метода newDirectoryStream которые возвращают объект типа DirectoryStream<Path> чтобы позволить нам перебирать записи в каталоге. Здесь мы видим явное различие между текущей и предыдущими версиями библиотеки ввода-вывода (возвращая простые массивы), предотвращая NullPointerException . Следующий пример показывает, как просто перечислить содержимое данного каталога:

01
02
03
04
05
06
07
08
09
10
11
Path directoryPath = Paths.get("C:", "Program Files/Java/jdk1.7.0_40/src/java/nio/file");
 
if (Files.isDirectory(directoryPath)) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath)) {
        for (Path path : stream) {
            System.out.println(path);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Обратите внимание на использование метода проверки isDirectory который предотвращает NotDirectoryException . Также обратите внимание на использование конструкции try-with-resourcesDirectoryStream является одновременно AutoCloseable и Closeable (что означает, что его необходимо закрыть в какое-то время), так что try-with-resources пригодится. Код возвращает следующий вывод:

01
02
03
04
05
06
07
08
09
10
11
...
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\CopyOption.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryIteratorException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryNotEmptyException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryStream.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\Files.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileStore.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystem.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemAlreadyExistsException.java
...

Чтобы обеспечить универсальное использование DirectoryStream<Path> мы можем фильтровать, используя два основных механизма:

  • newDirectoryStream(Path dir, String glob)
    • Фильтрация с использованием GLOB
  • newDirectoryStream (Path dir, DirectoryStream.Filterfilter)
    • Фильтрация с использованием DirectoryStream.Filter

Фильтрация по шаблону GLOB

Прежде всего нам нужно знать, что такое ГЛОБ. Шаблоны GLOB — это строковые выражения, которые следуют определенным правилам синтаксиса и используются для сопоставления. Пожалуйста, обратитесь к следующей статье для получения дополнительной информации о GLOB и синтаксисе GLOB . Когда дело доходит до фильтрации с помощью GLOB, класс Files предоставляет нам простой способ сделать это. Давайте посмотрим на следующий пример.

01
02
03
04
05
06
07
08
09
10
11
Path directoryPath = Paths.get("C:", "Program Files/Java/jdk1.7.0_40/src/java/nio/file");
 
if (Files.isDirectory(directoryPath)) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath, "File*Exception*")) {
        for (Path path : stream) {
            System.out.println(path);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Со следующим выводом:

1
2
3
4
5
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemLoopException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileSystemNotFoundException.java

Фильтрация с помощью DirectoryStream.Filter

Когда для выполняемой задачи требуются более сложные параметры фильтрации, а не просто сопоставление имен файлов, нам необходимо реализовать интерфейс DirectoryStream.Filter<Path> . Это самый мощный вариант фильтрации, доступный в нашем распоряжении, так как у нас есть доступ к остальной части приложения и мы можем использовать сторонние библиотеки. В следующем примере показана такая ситуация с двумя условиями фильтрации:

  • Размер файла должен быть четным числом
  • Время выполнения в миллисекундах должно быть четным числом
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Path directoryPath = Paths.get("C:", "Program Files/Java/jdk1.7.0_40/src/java/nio/file");
DirectoryStream.Filter<Path> filter = new Filter<Path>() {
 
    @Override
    public boolean accept(Path entry) throws IOException {
        long size = Files.readAttributes(entry, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS).size();
        long milis = new Date().getTime();
 
        boolean isSizeEvenNumber = size % 2 == 0;
        boolean isTheTimeRight = milis % 2 == 0;
 
        return isTheTimeRight && isSizeEvenNumber;
    }
};
 
if (Files.isDirectory(directoryPath)) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directoryPath, filter)) {
        for (Path path : stream) {
            System.out.println(path);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Со следующим выводом:

1
2
3
4
5
6
7
8
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\DirectoryStream.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\FileAlreadyExistsException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\Files.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\NotDirectoryException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\NotLinkException.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\package-info.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\WatchEvent.java
C:\Program Files\Java\jdk1.7.0_40\src\java\nio\file\WatchService.java

* Обратите внимание, что в зависимости от используемых условий отфильтрованные файлы могут различаться в зависимости от исполнения.