How to get the file path of a file - java

If I already have an existing file and I want to know its path using only its name, how can I do this?
I have the following code, but it return the name of the file even when it does not exists:
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.{java,class}");
Path filename = Paths.get("Go,mvDep.java");
if (matcher.matches(filename)) {
System.out.println(filename);
}
Thank you for your help!

I think the core of your confusion is that a Path does not necessarily represent a file on your computer. It is simply a Java object that represents a conceptual object in a file system. In the same way that you could construct a new Person("John", "Smith") without actually knowing anyone named 'John Smith', you can construct a Path regardless of whether or not a file exists at the given location.
Once you have a Path there are a number of things you can do with it, including check if it exists via Files.exists(), or create it with Files.createFile(). Generally speaking, the Files class lets you inspect and work with the actual file system objects a Path represents.
The intent of a PathMatcher is similarly disconnected from the actual file system; it exists to to determine if a given Path fits the PathMatcher's pattern - it's basically a Path-specific regular expression engine.
So what your code is actually doing is:
Creating a glob that will match any path which ends in .java or .class (regardless of whether such a path exists anywhere).
Constructing a relative Path to a file called Go,mvDep.java. Implicitly this path is relative to the current working directory, but you could pass it to Path.resolve() to create a new Path referring to a file in a different location.
Checking if the path Go,mvDep.java matches your glob, which it does since it ends in .java, so it prints the path.
It sounds like what you actually want is to find an existing file with the name Go,mvDep.java. If so, you want to use Files.find() to search a directory and return a stream of the files that match a BiPredicate<Path, BasicFileAttributes> matcher you define. Your matcher might look something like this:
new BiPredicate<Path, BasicFileAttributes>() {
public boolean test(Path path, BasicFileAttributes attributes) {
return matcher.matches(path);
}
}
Or in Lambda syntax simply:
(p, a) -> matcher.matches(p)

Related

Java 7 glob issue

As much I want, I can't truly understand how to deal with glob in java.
below a short program:
public class Test{
public static void main(String args[]) throws IOException{
Path p1 = Paths.get("C:\\Users\\all\\Test\\cool.jpg");
Path p2 = Paths.get("cool.jpg");
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**/*.jpg")
System.out.println(matcher.matches(p1));
System.out.println(matcher.matches(p2));
}
}
The result is TRUE / FALSE
Running with "glob: *.jpg" the result is FALSE
/ TRUE
So it looks like first time it search outside of the folder were is located the project (java file) and second time in the folder were is locate the java file.
If I change the second path to any other location (other than my java project is located) the result is: for glob:**/*.jpg -> TRUE/TRUE and for glob: *.jpg -> FALSE/FALSE.
So my question is: I understood well that using glob:*.??? it search in the folder were java project is located and using glob:**/*.??? it search to a specific path other than the folder were java project is located. Because in the documentation is written:
A glob pattern is specified as a string and is matched against other strings, such as directory or file names. Glob syntax follows several simple rules:
An asterisk, *, matches any number of characters (including none).
Two asterisks, **, works like * but crosses directory boundaries. This syntax is generally used for matching complete paths.
But nowhere is mentioned that the directory boundaries referring to the directory where Java project is located.
Change the glob pattern to this:
"glob:**.jpg"
Your pattern matches only file paths containing at least one directory.
For example following pattern matches only paths with at least two directories (ex path: a/b/image.jpg)
"glob:**/**/*.jpg"
And just FYI it does not search the files, it only tests whether given path matches the pattern.

Java nio: How to add extension to an absolute path?

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");

How to get the absolute path of a file when given a relative or absolute path and the absolute path it is relative to

Let's say I have an absolute 'base' path:
/home/someone/dir1/dir2/
The user can pass me a new path, that can either be absolute or relative to base path, so the following would both be valid:
..
/home/someone/dir1/
How do I get java to give me the correct absolute path ie for both these cases:
/home/someone/dir1/
and do this in a platform-independent way?
I tried the following:
File resolvedFile = new File((new File(basePath).toURI().resolve(new File(newPath).toURI())));
However, where newPath was relative, newFile(newPath) resolves it automatically against the current working directory, rather than the basePath I want to supply.
Any thoughts?
Many thanks!
Answering my own question..
Seems like it can be done in java 7 using Path:
Path p1 = Paths.get("/home/joe/foo");
// Result is /home/joe/foo/bar
System.out.format("%s%n", p1.resolve("bar"));
Since I can't get java 7 for my mac 10.5.8, I'm going with something like (NB NOT THOROUGHLY TESTED!):
static String getAbsolutePath(String basePath, String relativeOrAbsolutePath) throws IOException {
boolean isAbsolute = false;
File relativeOrAbsoluteFile = new File(relativeOrAbsolutePath);
if (relativeOrAbsoluteFile.isAbsolute()){
isAbsolute = true;
}
if (isAbsolute){
return relativeOrAbsolutePath;
}
else {
File absoluteFile = new File(basePath, relativeOrAbsolutePath);
return absoluteFile.toString();
}
}
Take a look at File#getCanonicalPath
From the JavaDocs:
Returns the canonical pathname string of this abstract pathname. A
canonical pathname is both absolute and unique. The precise definition
of canonical form is system-dependent. This method first converts this
pathname to absolute form if necessary, as if by invoking the
getAbsolutePath() method, and then maps it to its unique form in a
system-dependent way. This typically involves removing redundant names
such as "." and ".." from the pathname, resolving symbolic links (on
UNIX platforms), and converting drive letters to a standard case (on
Microsoft Windows platforms).
Every pathname that denotes an existing file or directory has a unique
canonical form. Every pathname that denotes a nonexistent file or
directory also has a unique canonical form. The canonical form of the
pathname of a nonexistent file or directory may be different from the
canonical form of the same pathname after the file or directory is
created. Similarly, the canonical form of the pathname of an existing
file or directory may be different from the canonical form of the same
pathname after the file or directory is deleted.
try this in your code.
System.setProperty("user.dir", "your_base_path")
Not sure if this works outside of my setup (windows platform, JRE 1.6.x)
but the following worked like a trick:
File path = new File(relativeOrAbsoluteGoldpath);
absolutePath = path.getCanonicalPath();
where relativeOrAbsoluteGoldpath is an arbitrary path name that may or may not be relative.

java.io.File(parent, child) does not work as expected

I am trying to construct a Java File object based on a user provided file name (could be absolute or relative) and a environment dependent base directory. The java doc for java.io.File(File parent, String child) says the following:
If the child pathname string is absolute then it is converted into a relative pathname in a system-dependent way.
That made me think that if I have the following code:
public class TestClass {
public static void main(String[] args) throws IOException {
File file = new File(new File("C:/Temp"),"C:/Temp/file.txt");
System.out.println(file.getAbsolutePath());
}
}
the output would be
C:\Temp\file.txt
and then I'd be in business because it would not really matter anymore if the user provided an absolute or relative path. But in fact, the output is
C:\Temp\C:\Temp\file.txt
Which means I have to figure out the exact relative path (or at least test different options to see if the file exists). Am I misunderstanding the JavaDoc?
If the child pathname string is absolute then it is converted into a relative pathname in a system-dependent way.
I assume this means that even if you provide an absolute path, it will be converted to (in a system dependent way), and treated as, a relative path.
Which means I have to figure out the exact relative path (or at least test different options to see if the file exists).
Yes, I believe so.
This could perhaps be easily done with
file.getAbsolutePath().startsWith(parent.getAbsolutePath());
to check if it is an absolute path to a directory in parent, and
file.getAbsolutePath().substring(parent.getAbsolutePath().length());
to get the relative part.
With your base directory expressed as a Path, e.g.
Path basePath = new File("C:/Temp").toPath();
you can use Path.resolve to determine the Path (or File) for your fileName, regardless of whether it is absolute or relative:
Path path = basePath.resolve(fileName).normalize();
File file = path.toFile();
The additional normalize here simply takes care of cleaning up any . or .. from the path.

What's the difference between getPath(), getAbsolutePath(), and getCanonicalPath() in Java?

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.

Categories