API Java NIO2 Path

API Java NIO2 Path

1. обзор

В этой статье мы узнаем, как использовать новый API ввода-вывода (NIO2)Path в Java.

APIPath в NIO2 составляют одну из основных новых функциональных областей, поставляемых с Java 7, и, в частности, подмножество API новой файловой системы наряду с API файлов.

2. Настроить

Поддержка NIO2 входит в пакетjava.nio.file. Итак, настройка вашего проекта для использования APIPath - это просто вопрос импорта всего в этом пакете:

import java.nio.file.*;

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

private static String HOME = System.getProperty("user.home");

Эта переменная будет указывать на допустимое местоположение в любой среде.

КлассPaths является основной точкой входа для всех операций, связанных с путями файловой системы. Это позволяет нам создавать и манипулировать путями к файлам и каталогам.

Следует отметить, что операции с путями в основном имеют синтаксический характер; они не влияют на базовую файловую систему, и файловая система никак не влияет на их успех или неудачу. Это означает, что передача несуществующего пути в качестве параметра операции пути не имеет никакого отношения к тому, успешен он или нет.

3. Путь Операции

В этом разделе мы познакомимся с основным синтаксисом, используемым в операциях пути. Как следует из названия, классPath является программным представлением пути в файловой системе.

ОбъектPath содержит имя файла и список каталогов, используемых для построения пути, и используется для проверки, поиска и управления файлами.

Вспомогательный классjava.nio.file.Paths (во множественном числе) - это формальный способ создания объектовPath. Он имеет два статических метода для созданияPath из строки пути:

Path path = Paths.get("path string");

Используем ли мы прямую или обратную косую черту в путиString,, не имеет значения, API разрешает этот параметр в соответствии с требованиями базовой файловой системы.

И из объектаjava.net.URI:

Path path = Paths.get(URI object);

Теперь мы можем пойти дальше и увидеть это в действии.

4. Создание пути

К объектуcreate a Path из строки пути:

@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles/example");

    assertEquals("\\articles\\example", p.toString());
}

APIget может принимать параметр переменных аргументов частей строки пути (в данном случаеarticles иexample) в дополнение к первой части (в данном случаеarticles ).

Если мы предоставим эти части вместо полной строки пути, они будут использоваться для создания объекта Path, нам не нужно включать разделители имен (косые черты) в часть переменных аргументов:

@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles", "example");

    assertEquals("\\articles\\example", p.toString());
}

5. Получение информации о пути

Вы можете думать об объекте Path как об элементах имени как о последовательности. ПутьString, такой какE:\examplerticles\java, состоит из трех элементов имени, т.е. example,articles иjava. Самый высокий элемент в структуре каталогов будет располагаться под индексом 0, в данном случаеexample.

Самый нижний элемент в структуре каталогов будет располагаться по индексу[n-1], гдеn - это количество элементов имени в пути. Этот самый нижний элемент называетсяfile name независимо от того, является ли он фактическим файлом или нет:

@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");

    Path fileName = p.getFileName();

    assertEquals("logs", fileName.toString());
}

Методы доступны для извлечения отдельных элементов по индексу:

@Test
public void givenPath_whenRetrievesNameByIndex_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");
    Path name0 = getName(0);
    Path name1 = getName(1);
    Path name2 = getName(2);
    assertEquals("articles", name0.toString());
    assertEquals("example", name1.toString());
    assertEquals("logs", name2.toString());
}

или подпоследовательность пути, используя эти диапазоны индекса:

@Test
public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");

    Path subPath1 = p.subpath(0,1);
    Path subPath2 = p.subpath(0,2);

    assertEquals("articles", subPath1.toString());
    assertEquals("articles\\example", subPath2.toString());
    assertEquals("articles\\example\\logs", p.subpath(0, 3).toString());
    assertEquals("example", p.subpath(1, 2).toString());
    assertEquals("example\\logs", p.subpath(1, 3).toString());
    assertEquals("logs", p.subpath(2, 3).toString());
}

Каждый путь связан с родительским путем илиnull, если у пути нет родителя. Родительский объект объекта пути состоит из корневого компонента пути, если таковой имеется, и каждого элемента в пути, кроме имени файла. Например, родительский путь/a/b/c равен/a/b, а путь/a равен нулю:

@Test
public void givenPath_whenRetrievesParent_thenCorrect() {
    Path p1 = Paths.get("/articles/example/logs");
    Path p2 = Paths.get("/articles/example");
    Path p3 = Paths.get("/articles");
    Path p4 = Paths.get("/");

    Path parent1 = p1.getParent();
    Path parent2 = p2.getParent();
    Path parent3 = p3.getParent();
    Path parent4 = p4.getParenth();

    assertEquals("\\articles\\example", parent1.toString());
    assertEquals("\\articles", parent2.toString());
    assertEquals("\\", parent3.toString());
    assertEquals(null, parent4);
}

Мы также можем получить корневой элемент пути:

@Test
public void givenPath_whenRetrievesRoot_thenCorrect() {
    Path p1 = Paths.get("/articles/example/logs");
    Path p2 = Paths.get("c:/articles/example/logs");

    Path root1 = p1.getRoot();
    Path root2 = p2.getRoot();

    assertEquals("\\", root1.toString());
    assertEquals("c:\\", root2.toString());
}

6. Нормализация пути

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

Например, рассмотрим следующие строки пути:

/example/./articles
/example/authors/../articles
/example/articles

Все они разрешаются в одно и то же место/example/articles. Первые два имеют избыточность, а последний нет.

Нормализация пути предполагает устранение в нем избыточности. Для этого предусмотрена операцияPath.normalize().

Этот пример теперь должен быть понятен:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
    Path p = Paths.get("/home/./example/articles");

    Path cleanPath = p.normalize();

    assertEquals("\\home\\example\\articles", cleanPath.toString());
}

Этот тоже:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
    Path p = Paths.get("/home/example/../articles");

    Path cleanPath = p.normalize();

    assertEquals("\\home\\articles", cleanPath.toString());
}

7. Конверсия пути

Существуют операции для преобразования пути в выбранный формат представления. Чтобы преобразовать любой путь в строку, которую можно открыть из браузера, мы используем методtoUri:

@Test
public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
    Path p = Paths.get("/home/example/articles.html");

    URI uri = p.toUri();
    assertEquals(
      "file:///E:/home/example/articles.html",
        uri.toString());
}

Мы также можем преобразовать путь в его абсолютное представление. МетодtoAbsolutePath разрешает путь к каталогу файловой системы по умолчанию:

@Test
public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
    Path p = Paths.get("/home/example/articles.html");

    Path absPath = p.toAbsolutePath();

    assertEquals(
      "E:\\home\\example\\articles.html",
        absPath.toString());
}

Однако когда обнаруживаемый путь уже определен как абсолютный, метод возвращает его как есть:

@Test
public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
    Path p = Paths.get("E:\\home\\example\\articles.html");

    Path absPath = p.toAbsolutePath();

    assertEquals(
      "E:\\home\\example\\articles.html",
        absPath.toString());
}

Мы также можем преобразовать любой путь в его реальный эквивалент, вызвав методtoRealPath. Этот метод пытается разрешить путь, сопоставляя его элементы с реальными каталогами и файлами в файловой системе.

Пришло время использовать переменную, которую мы создали в разделеSetup, которая указывает на домашнее местоположение вошедшего в систему пользователя в файловой системе:

@Test
public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() {
    Path p = Paths.get(HOME);

    Path realPath = p.toRealPath();

    assertEquals(HOME, realPath.toString());
}

Приведенный выше тест мало что говорит нам о поведении этой операции. Наиболее очевидный результат состоит в том, что если путь не существует в файловой системе, операция выдастIOException, чтение продолжается.

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

@Test(expected = NoSuchFileException.class)
public void givenInExistentPath_whenFailsToConvert_thenCorrect() {
    Path p = Paths.get("E:\\home\\example\\articles.html");

    p.toRealPath();
}

Тест считается успешным, когда мы ловимIOException. Фактический подклассIOException, который генерирует эта операция, -NoSuchFileException.

8. Объединение путей

Соединить любые два пути можно с помощью методаresolve.

Проще говоря, мы можем вызвать методresolve для любогоPath и передатьpartial path в качестве аргумента. Этот частичный путь добавляется к исходному пути:

@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("java");

    assertEquals("\\example\\articles\\java", p2.toString());
}

Однако, когда строка пути, переданная методуresolve, не являетсяpartial path;, особенно абсолютным путем, тогда возвращается переданный путь:

@Test
public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("C:\\example\\articles\java");

    assertEquals("C:\\example\\articles\\java", p2.toString());
}

То же самое происходит с любым путем, который имеет корневой элемент. Строка пути“java” не имеет корневого элемента, а строка пути“/java” имеет корневой элемент. Поэтому, когда вы передаете путь с корневым элементом, он возвращается как есть:

@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("/java");

    assertEquals("\\java", p2.toString());
}

9. Relativizing Пути

Терминrelativizing просто означает создание прямого пути между двумя известными путями. Например, если у нас есть каталог/example и внутри него, у нас есть два других каталога, так что/example/authors и/example/articles являются допустимыми путями.

Путь кarticles относительноauthors будет описан как“move one level up in the directory hierarchy then into articles directory” или..rticles:

@Test
public void givenSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("articles");
    Path p2 = Paths.get("authors");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);

    assertEquals("..\\authors", p1_rel_p2.toString());
    assertEquals("..\\articles", p2_rel_p1.toString());
}

Предположим, мы перемещаем каталогarticles в папкуauthors, чтобы они больше не были братьями и сестрами. Следующие операции релятивизации включают создание пути междуexample иarticles и наоборот:

@Test
public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("/example");
    Path p2 = Paths.get("/example/authors/articles");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);

    assertEquals("authors\\articles", p1_rel_p2.toString());
    assertEquals("..\\..", p2_rel_p1.toString());
}

10. Сравнение путей

КлассPath имеет интуитивно понятную реализацию методаequals, который позволяет нам сравнивать два пути на равенство:

@Test
public void givenTwoPaths_whenTestsEquality_thenCorrect() {
    Path p1 = Paths.get("/example/articles");
    Path p2 = Paths.get("/example/articles");
    Path p3 = Paths.get("/example/authors");

    assertTrue(p1.equals(p2));
    assertFalse(p1.equals(p3));
}

Вы также можете проверить, начинается ли путь с данной строки:

@Test
public void givenPath_whenInspectsStart_thenCorrect() {
    Path p1 = Paths.get("/example/articles");

    assertTrue(p1.startsWith("/example"));
}

Или заканчивается другой строкой:

@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
    Path p1 = Paths.get("/example/articles");

    assertTrue(p1.endsWith("articles"));
}

11. Заключение

В этой статье мы продемонстрировали операции Path в новом API файловой системы (NIO2), который был поставлен как часть Java 7, и увидели, что большинство из них работают.

Примеры кода, использованные в этой статье, можно найти вGithub project статьи.