With Java NIO Path objects:
If I have a base path b and a relative path r, to get the full path f I can call b.resolve(r).
If I have the full path f and the base path b, to get the relative path r I can call b.relativize(f).
But what can I do if I have f and r, and want to find b?
Looking over the Path API, I can't see any simple/straightforward solution. The best I've been able to come up with is to simultaneously iterate over getParent() for both f and r until r' is empty/null, then f' should be b. But that seems clunky and inefficient.
I also tried a solution based on f.subpath() but that method strips the root component (e.g. C:\).
To retrieve the base path, you can use subpath() by passing as begin index 0and as end index the difference of path elements between the full path and the relative path, that is fullPath.getNameCount() - relativePath.getNameCount()
For example :
Path fullPath = Paths.get("C:/folder1/folder2/a/b/c.txt");
Path relativePath = Paths.get("b/c.txt");
Path basePath = fullPath.getRoot().resolve(fullPath.subpath(0, fullPath.getNameCount() - relativePath.getNameCount()));
System.out.println("basePath=" + basePath);
output :
basePath=C:\folder1\folder2\a
Note that fullPath.getRoot().resolve() is required because Windows doesn't consider a token with : as a path element in its subpath() implementation.
So in the actual example, C:\ will never be returned by subpath().
C:\ is considered in the Windows implementation as the root component.
As a general note, even if our application run on an Unix based OS, we should keep it to be not OS dependent. The OS where the JVM runs may be different in the future.
Related
I would like to compare two paths in the same workspace folder, and get the relative path between them :
String firPath = "C:/toto/tata/test1/test2/img/1.jpg" // test example
String secPath = "C:/toto/tata/test1/img/1.jpg" // test example
And return firstPath relative path from secondPath
example = "../../img/"
I found lots of example in different language (python, .net, c++ ...) :
How to get relative path from absolute path
compare path and get relative path between two files with javascript
...but no solution with java.
Most of the time, what is use are libraries methods, and I was wondering if java had the same methods I could use.
Thank you for your help.
What about added in Java 7 Path.relativize?
Path first = Paths.get(firstPath); Path second = Paths.get(secondPath);
System.out.println(first.relativize(second));
System.out.println(second.relativize(first));
This feels like it should be something straight forward, but I can seem to find an elegant solution to it without converting to File.
Given a Path
Path path = Paths.get("/a/b/foo")
How to do get the path /a/b/foo.bar? subpath will return a relative path regardless of whether the original path is relative or absolute.
I would prefer not to have to use additional libraries. But, maybe that is the only way?
To change the file name of a Path, use one of the resolveSibling() methods:
This is useful where a file name needs to be replaced with another file name.
Using this method ensures that the result Path object is for the same FileSystem as the source Path object.
So, to add extension ".bar" to a Path:
path = path.resolveSibling(path.getFileName() + ".bar");
This problem is driving me crazy. I have a file I would like to reach in my src/main/resources folder and I am trying to obtain the path via:
FileSystem fileSystem = FileSystems.getDefault();
Path path = fileSystem.getPath(AnalysisEngine.class.getResource("/models/10_NB_7dev_2.model").getFile());
However, I keep getting the following error:
Illegal char <:> at index 2: /C:/Users/...(the path is here)/models/10_NB_7dev_2.model
As you can see, the path returned has '/' before C:, which ruins everything. What is the reason and how could this be fixed? Is there an alternative with java.io package?
I am using Windows 8 - 64 bit OS, if it helps.
The URL returned by Class#getResource(String) contains a preceding /.
/C:/Users/...(the path is here)/models/10_NB_7dev_2.model
That's just how URLs work. Then the FileSystem tries to parse that, but it makes no sense to it that there is a : character in the mix, so it throws an exception. In other words, getPath() is trying to create a path, not a url. You cannot have a : character in a Windows (possibly linux as well) path, unless it is directly following the Drive name as the first two characters of the path string.
The solution here is not to use the path of a classpath resource. A classpath resource might not come from the filesystem directly, it might be inside a jar.
...(the path is here)/models/10_NB_7dev.model
in your code you put:
("/models/10_NB_7dev_2.model").
Are you meaning to put a _2.?
If you are not worried about using the default filesystem (e.g. if you aren't using an in-memory filesystem for testing) then you can do:
URI uri = AnalysisEngine.class.getResource("/models/10_NB_7dev_2.model").toURI();
Path path = Paths.get(uri);
Java 7 introduced java.nio.file.Path as a possible replacement for java.io.File.
With File, when I access a file under a specific, I would do:
File parent = new File("c:\\tmp");
File child = new File(parent, "child"); // this accesses c:\tmp\child
What's the way to do this with Path?
I supposed this will work:
Path parent = Paths.get("c:\\tmp");
Path child = Paths.get(parent.toString(), "child");
But calling parent.toString() seems ugly. Is there a better way?
Use the resolve method on Path.
There are two methods with this name. One takes a relative Path and the other a String. It uses the Path on which it is called as a parent and appends the String or relative Path appropriately.
Path parent = Paths.get("c:\\tmp");
Path child = parent.resolve("child");
To anyone finding this question looking specifically only for files that are within the specified path, you must be aware of path traversal attacks.
See: Filtering upwards path traversal in Java (or Scala)
It is critical that you check that the path starts with the root.
Path parent = Paths.get("C:\\tmp");
Path child = parent.resolve("chlid").normalize();
if (!child.startsWith(parent)) {
throw new IllegalArgumentException("Potential Path Traversal Attack");
}
What's the difference between getPath(), getAbsolutePath(), and getCanonicalPath() in Java?
And when do I use each one?
Consider these filenames:
C:\temp\file.txt - This is a path, an absolute path, and a canonical path.
.\file.txt - This is a path. It's neither an absolute path nor a canonical path.
C:\temp\myapp\bin\..\\..\file.txt - This is a path and an absolute path. It's not a canonical path.
A canonical path is always an absolute path.
Converting from a path to a canonical path makes it absolute (usually tack on the current working directory so e.g. ./file.txt becomes c:/temp/file.txt). The canonical path of a file just "purifies" the path, removing and resolving stuff like ..\ and resolving symlinks (on unixes).
Also note the following example with nio.Paths:
String canonical_path_string = "C:\\Windows\\System32\\";
String absolute_path_string = "C:\\Windows\\System32\\drivers\\..\\";
System.out.println(Paths.get(canonical_path_string).getParent());
System.out.println(Paths.get(absolute_path_string).getParent());
While both paths refer to the same location, the output will be quite different:
C:\Windows
C:\Windows\System32\drivers
The best way I have found to get a feel for things like this is to try them out:
import java.io.File;
public class PathTesting {
public static void main(String [] args) {
File f = new File("test/.././file.txt");
System.out.println(f.getPath());
System.out.println(f.getAbsolutePath());
try {
System.out.println(f.getCanonicalPath());
}
catch(Exception e) {}
}
}
Your output will be something like:
test\..\.\file.txt
C:\projects\sandbox\trunk\test\..\.\file.txt
C:\projects\sandbox\trunk\file.txt
So, getPath() gives you the path based on the File object, which may or may not be relative; getAbsolutePath() gives you an absolute path to the file; and getCanonicalPath() gives you the unique absolute path to the file. Notice that there are a huge number of absolute paths that point to the same file, but only one canonical path.
When to use each? Depends on what you're trying to accomplish, but if you were trying to see if two Files are pointing at the same file on disk, you could compare their canonical paths. Just one example.
In short:
getPath() gets the path string that the File object was constructed with, and it may be relative current directory.
getAbsolutePath() gets the path string after resolving it against the current directory if it's relative, resulting in a fully qualified path.
getCanonicalPath() gets the path string after resolving any relative path against current directory, and removes any relative pathing (. and ..), and any file system links to return a path which the file system considers the canonical means to reference the file system object to which it points.
Also, each of these has a File equivalent which returns the corresponding File object.
Note that IMO, Java got the implementation of an "absolute" path wrong; it really should remove any relative path elements in an absolute path. The canonical form would then remove any FS links or junctions in the path.
getPath() returns the path used to create the File object. This return value is not changed based on the location it is run (results below are for windows, separators are obviously different elsewhere)
File f1 = new File("/some/path");
String path = f1.getPath(); // will return "\some\path"
File dir = new File("/basedir");
File f2 = new File(dir, "/some/path");
path = f2.getPath(); // will return "\basedir\some\path"
File f3 = new File("./some/path");
path = f3.getPath(); // will return ".\some\path"
getAbsolutePath() will resolve the path based on the execution location or drive. So if run from c:\test:
path = f1.getAbsolutePath(); // will return "c:\some\path"
path = f2.getAbsolutePath(); // will return "c:\basedir\some\path"
path = f3.getAbsolutePath(); // will return "c:\test\.\basedir\some\path"
getCanonicalPath() is system dependent. It will resolve the unique location the path represents. So if you have any "."s in the path they will typically be removed.
As to when to use them. It depends on what you are trying to achieve. getPath() is useful for portability. getAbsolutePath() is useful to find the file system location, and getCanonicalPath() is particularly useful to check if two files are the same.
The big thing to get your head around is that the File class tries to represent a view of what Sun like to call "hierarchical pathnames" (basically a path like c:/foo.txt or /usr/muggins). This is why you create files in terms of paths. The operations you are describing are all operations upon this "pathname".
getPath() fetches the path that the File was created with (../foo.txt)
getAbsolutePath() fetches the path that the File was created with, but includes information about the current directory if the path is relative (/usr/bobstuff/../foo.txt)
getCanonicalPath() attempts to fetch a unique representation of the absolute path to the file. This eliminates indirection from ".." and "." references (/usr/foo.txt).
Note I say attempts - in forming a Canonical Path, the VM can throw an IOException. This usually occurs because it is performing some filesystem operations, any one of which could fail.
I find I rarely have need to use getCanonicalPath() but, if given a File with a filename that is in DOS 8.3 format on Windows, such as the java.io.tmpdir System property returns, then this method will return the "full" filename.