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 статьи.