API Java NIO2 Path

1. Panoramica

In questo articolo, impareremo come utilizzare la nuova API Path I / O (NIO2) in Java.

Le API Path in NIO2 costituiscono una delle principali nuove aree funzionali fornite con Java 7 e in particolare un sottoinsieme della nuova API del file system insieme alle API dei file.

2. Configurazione

Il supporto NIO2 è incluso nel pacchetto java.nio.file . Quindi impostare il tuo progetto per utilizzare le API Path è solo questione di importare tutto in questo pacchetto:

import java.nio.file.*;

Poiché gli esempi di codice in questo articolo verranno probabilmente eseguiti in ambienti diversi, prendiamo un handle sulla directory home dell'utente:

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

Questa variabile punterà a una posizione valida in qualsiasi ambiente.

La classe Paths è il punto di ingresso principale per tutte le operazioni che coinvolgono i percorsi del file system. Ci permette di creare e manipolare percorsi a file e directory.

Degno di nota è che le operazioni sui percorsi sono principalmente di natura sintattica; non hanno alcun effetto sul file system sottostante e nemmeno il file system ha alcun effetto sul fatto che abbiano successo o meno. Ciò significa che il passaggio di un percorso inesistente come parametro di un'operazione di percorso non ha alcuna rilevanza sul fatto che abbia successo o meno.

3. Operazioni sui percorsi

In questa sezione, introdurremo la sintassi principale utilizzata nelle operazioni sui percorsi. Come suggerisce il nome, la classe Path è una rappresentazione programmatica di un percorso nel file system.

Un oggetto Path contiene il nome del file e l'elenco delle directory utilizzati per costruire il percorso e viene utilizzato per esaminare, individuare e manipolare i file.

La classe helper, java.nio.file.Paths (in forma plurale) è il modo formale di creare oggetti Path . Ha due metodi statici per creare un percorso da una stringa di percorso:

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

Indipendentemente dal fatto che utilizziamo una barra in avanti o una barra rovesciata nel percorso String, non importa, l'API risolve questo parametro in base ai requisiti del file system sottostante.

E da un oggetto java.net.URI :

Path path = Paths.get(URI object);

Ora possiamo andare avanti e vederli in azione.

4. Creazione di un percorso

Per creare un oggetto Path da una stringa di percorso:

@Test public void givenPathString_whenCreatesPathObject_thenCorrect() { Path p = Paths.get("/articles/baeldung"); assertEquals("\\articles\\baeldung", p.toString()); }

L' API get può accettare un parametro di argomenti variabili delle parti della stringa di percorso (in questo caso, articoli e baeldung ) oltre alla prima parte (in questo caso, articoli ).

Se forniamo queste parti invece di una stringa di percorso completa, verranno utilizzate per costruire l'oggetto Path, non è necessario includere i separatori di nomi (barre) nella parte degli argomenti della variabile:

@Test public void givenPathParts_whenCreatesPathObject_thenCorrect() { Path p = Paths.get("/articles", "baeldung"); assertEquals("\\articles\\baeldung", p.toString()); }

5. Recupero delle informazioni sul percorso

Puoi pensare all'oggetto Path come agli elementi del nome come a una sequenza. Una stringa di percorso come E: \ baeldung \ articles \ java è composta da tre elementi nome, ovvero baeldung , articoli e java . L'elemento più alto nella struttura della directory sarebbe situato all'indice 0, in questo caso baeldung .

L'elemento più basso nella struttura della directory si trova all'indice [n-1] , dove n è il numero di elementi del nome nel percorso. Questo elemento più basso è chiamato nome del file indipendentemente dal fatto che si tratti di un file effettivo o meno:

@Test public void givenPath_whenRetrievesFileName_thenCorrect() { Path p = Paths.get("/articles/baeldung/logs"); Path fileName = p.getFileName(); assertEquals("logs", fileName.toString()); }

Sono disponibili metodi per recuperare singoli elementi in base all'indice:

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

o una sottosequenza del percorso utilizzando questi intervalli di indice:

@Test public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() { Path p = Paths.get("/articles/baeldung/logs"); Path subPath1 = p.subpath(0,1); Path subPath2 = p.subpath(0,2); assertEquals("articles", subPath1.toString()); assertEquals("articles\\baeldung", subPath2.toString()); assertEquals("articles\\baeldung\\logs", p.subpath(0, 3).toString()); assertEquals("baeldung", p.subpath(1, 2).toString()); assertEquals("baeldung\\logs", p.subpath(1, 3).toString()); assertEquals("logs", p.subpath(2, 3).toString()); }

Ogni percorso è associato a un percorso padre o null se il percorso non ha un percorso padre. Il genitore di un oggetto percorso è costituito dal componente radice del percorso, se presente, e da ogni elemento nel percorso ad eccezione del nome del file. Ad esempio, il percorso genitore di / a / b / c è / a / be quello di / a è nullo:

@Test public void givenPath_whenRetrievesParent_thenCorrect() { Path p1 = Paths.get("/articles/baeldung/logs"); Path p2 = Paths.get("/articles/baeldung"); 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\\baeldung", parent1.toString()); assertEquals("\\articles", parent2.toString()); assertEquals("\\", parent3.toString()); assertEquals(null, parent4); }

Possiamo anche ottenere l'elemento radice di un percorso:

@Test public void givenPath_whenRetrievesRoot_thenCorrect() { Path p1 = Paths.get("/articles/baeldung/logs"); Path p2 = Paths.get("c:/articles/baeldung/logs"); Path root1 = p1.getRoot(); Path root2 = p2.getRoot(); assertEquals("\\", root1.toString()); assertEquals("c:\\", root2.toString()); }

6. Normalizzare un percorso

Molti file system utilizzano "." notazione per denotare la directory corrente e ".." per denotare la directory padre. Potrebbe verificarsi una situazione in cui un percorso contiene informazioni di directory ridondanti.

Ad esempio, considera le seguenti stringhe di percorso:

/baeldung/./articles /baeldung/authors/../articles /baeldung/articles

They all resolve to the same location /baeldung/articles. The first two have redundancies while the last one does not.

Normalizing a path involves removing redundancies in it. The Path.normalize() operation is provided for this purpose.

This example should now be self-explanatory:

@Test public void givenPath_whenRemovesRedundancies_thenCorrect1() { Path p = Paths.get("/home/./baeldung/articles"); Path cleanPath = p.normalize(); assertEquals("\\home\\baeldung\\articles", cleanPath.toString()); }

This one too:

@Test public void givenPath_whenRemovesRedundancies_thenCorrect2() { Path p = Paths.get("/home/baeldung/../articles"); Path cleanPath = p.normalize(); assertEquals("\\home\\articles", cleanPath.toString()); }

7. Path Conversion

There are operations to convert a path to a chosen presentation format. To convert any path into a string that can be opened from the browser, we use the toUri method:

@Test public void givenPath_whenConvertsToBrowseablePath_thenCorrect() { Path p = Paths.get("/home/baeldung/articles.html"); URI uri = p.toUri(); assertEquals( "file:///E:/home/baeldung/articles.html", uri.toString()); }

We can also convert a path to its absolute representation. The toAbsolutePath method resolves a path against a file system default directory:

@Test public void givenPath_whenConvertsToAbsolutePath_thenCorrect() { Path p = Paths.get("/home/baeldung/articles.html"); Path absPath = p.toAbsolutePath(); assertEquals( "E:\\home\\baeldung\\articles.html", absPath.toString()); }

However, when the path to be resolved is detected to be already absolute, the method returns it as is:

@Test public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() { Path p = Paths.get("E:\\home\\baeldung\\articles.html"); Path absPath = p.toAbsolutePath(); assertEquals( "E:\\home\\baeldung\\articles.html", absPath.toString()); }

We can also convert any path to its real equivalent by calling the toRealPath method. This method tries to resolve the path by mapping it's elements to actual directories and files in the file system.

Time to use the variable we created in the Setup section which points to logged-in user's home location in the file system:

@Test public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() { Path p = Paths.get(HOME); Path realPath = p.toRealPath(); assertEquals(HOME, realPath.toString()); }

The above test does not really tell us much about the behavior of this operation. The most obvious result is that if the path does not exist in the file system, then the operation will throw an IOException, read on.

For the lack of a better way to drive this point home, just take a look at the next test, which attempts to convert an inexistent path to a real path:

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

The test succeeds when we catch an IOException. The actual subclass of IOException that this operation throws is NoSuchFileException.

8. Joining Paths

Joining any two paths can be achieved using the resolve method.

Simply put, we can call the resolve method on any Path and pass in a partial path as the argument. That partial path is appended to the original path:

@Test public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() { Path p = Paths.get("/baeldung/articles"); Path p2 = p.resolve("java"); assertEquals("\\baeldung\\articles\\java", p2.toString()); }

However, when the path string passed to the resolve method is not a partial path; most notably an absolute path, then the passed-in path is returned:

@Test public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() { Path p = Paths.get("/baeldung/articles"); Path p2 = p.resolve("C:\\baeldung\\articles\java"); assertEquals("C:\\baeldung\\articles\\java", p2.toString()); }

The same thing happens with any path that has a root element. The path string “java” has no root element while the path string “/java” has a root element. Therefore, when you pass in a path with a root element, it is returned as is:

@Test public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() { Path p = Paths.get("/baeldung/articles"); Path p2 = p.resolve("/java"); assertEquals("\\java", p2.toString()); }

9. Relativizing Paths

The term relativizing simply means creating a direct path between two known paths. For instance, if we have a directory /baeldung and inside it, we have two other directories such that /baeldung/authors and /baeldung/articles are valid paths.

The path to articles relative to authors would be described as “move one level up in the directory hierarchy then into articles directory” or ..\articles:

@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()); }

Assuming we move the articles directory to authors folder such that they are no longer siblings. The following relativizing operations involve creating a path between baeldung and articles and vice versa:

@Test public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() { Path p1 = Paths.get("/baeldung"); Path p2 = Paths.get("/baeldung/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. Comparing Paths

The Path class has an intuitive implementation of the equals method which enables us to compare two paths for equality:

@Test public void givenTwoPaths_whenTestsEquality_thenCorrect() { Path p1 = Paths.get("/baeldung/articles"); Path p2 = Paths.get("/baeldung/articles"); Path p3 = Paths.get("/baeldung/authors"); assertTrue(p1.equals(p2)); assertFalse(p1.equals(p3)); }

You can also check if a path begins with a given string:

@Test public void givenPath_whenInspectsStart_thenCorrect() { Path p1 = Paths.get("/baeldung/articles"); assertTrue(p1.startsWith("/baeldung")); }

Or ends with some other string:

@Test public void givenPath_whenInspectsEnd_thenCorrect() { Path p1 = Paths.get("/baeldung/articles"); assertTrue(p1.endsWith("articles")); }

11. Conclusion

In questo articolo, abbiamo mostrato le operazioni Path nella nuova API del file system (NIO2) che è stata fornita come parte di Java 7 e abbiamo visto la maggior parte di esse in azione.

Gli esempi di codice utilizzati in questo articolo sono disponibili nel progetto Github dell'articolo.