On Windows, file starting with / is not considered absolute - java

This surprised me, but apparently, a path on Windows that starts with a slash is not considered absolute. You need to specify c:. But surely, there is a difference between /foo and foo
System.out.println(new File(new File("/folder"), "abc"));
System.out.println(new File(new File("/folder"), "abc").isAbsolute());
\folder\abc
false
Is this correct behavior?
I want to be able to identify a relative path, such as foo, so I can prepend a default directory. But I would consider /foo to be an absolute path and would not prepend the default

You don't need to check for "isAbsolute" by string comparisons, just use Path and resolve using defaultDir.resolve(path)
Consider:
var rel = Path.of("relative");
var abs = Path.of("/absolute");
for (Path dir : List.of(Path.of("."), Path.of("abc"),Path.of("/abc"))) {
System.out.println("dir="+dir);
System.out.println("dir.resolve("+abs+")="+dir.resolve(abs));
System.out.println("dir.resolve("+rel+")="+dir.resolve(rel));
}
This handles resolving from the default directory path using another path which may be relative or absolute (in sense that it starts with path separator) on Windows or Linux.
Example run from Linux (Windows is same answer but with Windows path separator character).
dir=.
dir.resolve(/absolute)=/absolute
dir.resolve(relative)=./relative
dir=abc
dir.resolve(/absolute)=/absolute
dir.resolve(relative)=abc/relative
dir=/abc
dir.resolve(/absolute)=/absolute
dir.resolve(relative)=/abc/relative

You can check if the start of your file path begins with a "/" using Boolean isPathAbsolute = "/foo".startsWith("/") and use this variable to decide if you prepend the default or not!

Related

Is there a way to get the path of a File with "/" instead of "\"?

I am making an HTTP Server in Java so that (on start) it finds all files in a directory (and it's sub-directories) and adds them to the server. But when getting the path of a file and trying to give it to HttpServer.createContext(), it throws a java.lang.IllegalArgumentException: Illegal value for path or protocol. (with the string argument, say "\folder/index.html"). To get this value, I used
file.getParent().substring(24) + "/" + file.getName()
I used substring because I had to exclude the folder the web server is in. The illegal character is the backslash. I have tried extending File to change separator and separatorChar, but that only created 2 new variables. While using String.replace() didn't seem to have any effect. Is there a different method than File.getParent or File.getPath that I can use, or is there a way to use String.replace that I am not seeing?
EDIT:
String.replace() seems to be the best answer... But I am not completely sure how to use it.
EDIT 2: For some reason the backslash isn't showing up, so I changed it.
You have to use the java System.getProperty.
Notice that, in this context, "file.separator" is a key which we are
using to get this property from current system executing the java VM.
Insteady of using a slash (/), you should choose a platform agnostic file separator, as an example it should be:
String separator = System.getProperty("file.separator");
System.out.println(separator);
// unix / , windows \
Have a look at Paths.get(...)
Try Paths.get(".") // current working directory.
Or tell it, on which path it should start:
Use System.getProperty("user.dir"), for current loged in user, home directory.
String pathStr = "/";
Path homeDir = Paths.get(System.getProperty("user.dir"))
Getting from the user directory into the data directory: homeDir.get("data")
Path dataPath = Paths.get(System.getProperty("user.dir"));
File dataFile = dataPath.toFile();
Now use operations on dataFile, to check what files and directories there are, on that location of the file system.

Represent a `Path` starting with a `\` but without a disk drive, under Windows

I do not own a Windows copy, but would like to know the behavior and recommended usage in Java for representing a path such as \autoexec.bat under Windows?
Semantically, such a path would represent the file autoexec.bat on the root of any file system. Thus it would need to be resolved against a path representing a disk drive (such as C:\) before representing a file. In that sense, it is not absolute. However, it also does not have a root component, I suppose.
Can such path be created when running the JVM on Windows? If so, what would getRoot() and isAbsolute() return?
I tried the following code using Memory File System, but this throws InvalidPathException: “path must not start with separator at index 1: \truc”. Does this faithfully reflect the behavior under Windows, or is it a quirk of this specific library?
try (FileSystem fs = MemoryFileSystemBuilder.newWindows().build()) {
final Path truc = fs.getPath("\\truc");
LOGGER.info("Root: {}.", truc.getRoot());
LOGGER.info("Abs: {}.", truc.isAbsolute());
LOGGER.info("Abs: {}.", truc.toAbsolutePath());
}
Such paths are valid in the Windows terminal, or at least they were last time I used Windows (a long time ago). It would be handy to create such path in order to mark that the path is “absolute” (in the sense of starting with a backslash, thus not relative to a folder), but still leave the path without a driver letter specified. Then such a path can be (later) resolved to C:\autoexec.bat or D:\autoexec.bat or …
In Windows, \\ refers to the current drive which is C:\ in my case.
Not sure how the MemoryFileSystemBuilder works but the following code
File file = new File("\\test.txt");
final Path truc = file.toPath();
System.out.println("Root: " + truc.getRoot().toString());
System.out.println("Abs: " + truc.isAbsolute());
System.out.println("Abs: " + truc.toAbsolutePath().toString());
gives the below output
Root: \
Abs: false
Abs: C:\test.txt

Path.startsWith returns false for a Windows file path on Linux

Why would this be?
Path parent1 = Paths.get("/flugel/borf/noggin");
Path child1 = Paths.get("/flugel/borf/noggin/foo/bar/baz.jpg");
System.out.println("child1 startsWith parent1? " + child1.startsWith(parent1));
System.out.println(child1.getFileSystem());
System.out.println(parent1.getFileSystem());
Path parent2 = Paths.get("C:\\foo");
Path child2 = Paths.get("C:\\foo\\bar\\baz.jpg");
System.out.println("child2 startsWith parent2? " + child2.startsWith(parent2));
System.out.println(child2.getFileSystem());
System.out.println(parent2.getFileSystem());
returns
child1 startsWith parent1? true
sun.nio.fs.LinuxFileSystem#f5f2bb7
sun.nio.fs.LinuxFileSystem#f5f2bb7
child2 startsWith parent2? false
sun.nio.fs.LinuxFileSystem#f5f2bb7
sun.nio.fs.LinuxFileSystem#f5f2bb7
I'm running Java 8 on Ubuntu, but nothing about the javadocs for Path.startsWith explains why this occurs. Neither file path contains any actual files.
You have to check the code to see what is actually going on. So when you create a Path normalizeAndCheck function is called. In your case this is called on sun.nio.fs.UnixPath. Since path delimiter for *nix is / path strings will be normalized by /.
In case of Windows paths there are no / so they will stay exactly the same, so it will compare "C:\\foo" "C:\\foo\\bar\\baz.jpg" which are different strings and hence no common prefix.
I think below line from Java Docs of java.nio.file.Path answers your question
An object that may be used to locate a file in a file system. It will
typically represent a system dependent file path.
As described in the Javadocs, Java uses the "path separator" to determines the current operating environment path separator character. This can be accessed via:
System.getProperty("path.separator");
on UNIX based system it is "/", while on Windows systems it is "\". If you want to change these properties you can use the following to achieve that:
Properties p = System.getProperties();
p.put("path.separator", "\\");
System.setProperties(p);

java.nio.Path relativize between Paths does assumptions, which I cannot check

I am using the new Path object of java 7 and I am running into an issue.
I have a file storage system with a base directory and I create my own relative path. In the end I want to store just this relative path somewhere. I am running into a problem with Path.relativize though.
I have two usecases.
1.
Path baseDir = Paths.get("uploads");
Path filename = Paths.get("uploads/image/test.png")
return baseDir.relativize(filename);
This returns a Path image/test.png, which is perfect.
However, usecase 2:
Path baseDir = Paths.get("uploads");
Path filename = Paths.get("image/test.png")
return baseDir.relativize(filename);
returns ../image/test.png. I just want it to return "image/test.png"
In the Path tutorial it says
In the absence of any other information, it is assumed that 2 Paths are siblings
What I want is to be able to detect that this is the case. In this case, I want to just return the filename and ignore the baseDir.
I currently solve it like this, but I was hoping there was a better way:
Path rootEnding = getRootDirectory().getName(getRootDirectory().getNameCount() - 1);
for (Path part : path) {
if (part.equals(rootEnding)) {
return getRootDirectory().relativize(path);
}
}
return path;
So my question is, is there any better way of checking this?
Try adding a normalize() after relativize(). It seems to intended to do exactly this (remove unnecessary .. and . ). Don't miss the caution about symlinks in the javadoc.
This isn't 100% equivalent to what you wrote above, but I think it does what you want. Basically, let baseDir be a relative path. Pretend that whatever baseDir is relative to is the root of the file system. Then allow filename to be either relative or absolute from this "simulated root".
What about:
if (filename.startsWith(baseDir)) {
filename = baseDir.relativize(filename);
}

File.mkdir is not working and I can't understand why

I've this brief snippet:
String target = baseFolder.toString() + entryName;
target = target.substring(0, target.length() - 1);
File targetdir = new File(target);
if (!targetdir.mkdirs()) {
throw new Exception("Errore nell'estrazione del file zip");
}
doesn't mattere if I leave the last char (that is usually a slash). It's done this way to work on both unix and windows. The path is actually obtained from the URI of the base folder. As you can see from baseFolder.toString() (baseFolder is of type URI and is correct). The base folder actually exists. I can't debug this because all I get is true or false from mkdir, no other explanations.The weird thing is that baseFolder is created as well with mkdir and in that case it works.
Now I'm under windows.
the value of target just before the creation of targetdir is "file:/C:/Users/dario/jCommesse/jCommesseDB"
if I cut and paste it (without the last entry) in windows explore it works...
The path you provide is not a file path, but a URI.
I suggest you try the following :
URI uri = new URI("file://c:/foo/bar");
File f = new File(uri).
It looks, to me, as if the "file:/" at the beginning is the problem... Try getAbsolutePath() instead of toString().
The File constructor taking a String expects a path name. A path name is not an URI.
Remove the file:/ from the front of the String (or better yet, use getPath() instead of toString()) to get to the path you need.

Categories

Resources