How to create middle folders on an already existing path? - java

I have a path like this:
/foo/bar/... (can contain other subdirs or files)
I want to add a "middle" folder in the mid of said path, leading to:
/foo/middle/bar/...
What's an elegant way to do that?

Yeah so what you can do is have a path like the following that represents the absolute path of foo/bar/
Path path = Paths.get("foo", "bar");
Then you can get the parent of the current path.
Path parent = path.getParent();
Then you can resolve a sibling, a new path.
Path newChild = parent.resolve("middle");
Then you cna create that path if it doesnt exist.
Files.createDirectory(newChild);

Not sure what you have, or what you want, but assuming your path is a String
def path = "/foo/bar/baz/whee/yay"
You could just split the string on /
def segments = path.split('/')
insert one at position 2 (split has left us an empty entry before the initial /)
def inserted = segments[0..1] + 'new' + segments[2..-1]
Then join them back together
assert inserted.join('/') == '/foo/new/bar/baz/whee/yay'

Related

NIO2 Path Symbols Java

If I created a two paths such as:
Path path3 = Paths.get("E:\\data");
Path path4 = Paths.get("E:\\user\\home");
And then make a new Path(relativePath) by using the relativize() method on the two paths, creating: "..\user\home" does the path symbol(..) in this case refer to "data" or does it just indicate a relative path?
Path relativePath = path3.relativize(path4);
// ..\user\home <- output
So my Question is, what does the Path symbol (..) represent?
The relativize method needs two inputs but doesn't "secretly" encode the base path into it's output, so your relativePath has to be applied to another base path to actually access a path on disk.
But you can apply it to a different base path, e.g. if you want to sync two folder structures below two different base paths.
tl;dr: it just indicates a relative path.
But take care with your path separator: if you hardcode that into your path strings like in your example, it will fail on other systems. Better split up the individual parts in extra strings like this:
Path path4 = Paths.get("E:", "user", "home");

Getting an element inside a directory using java.nio.file.Path's

I am currently getting to grips with file management in Java. As far as i've read, java.nio.file.Path is the preferred way of doing so.
Say I want to copy the contents of oldDir to the, currently empty, newDir. Every time I copy a file, I need this loong line just to get the Path of newFile:
Path newDir = FileSystems.getDefault().getPath("new");
Path oldDir = FileSystems.getDefault().getPath("old");
for (Path oldFile : oldDir) {
Path newFile = FileSystems.getDefault().getPath("new", oldFile.getFileName().toString()); // Why so complicated? :(
Files.copy(oldFile, newFile);
}
Is there something like newDir.getChild(oldFile.getFileName()) to do what I want, or is there really no shorter way of doing it?
There are a couple things you can do to make the code simpler:
Use Path#of(String,String...) or Paths#get(String,String...) to create your Path instances. Both methods delegate to the default FileSystem. The former was added in Java 11 and is now the preferred approach.
Use Path#resolve(Path) to append a relative path to some absolute path.
But there's also an error in your code. You are iterating over oldDir which works because Path implements Iterable<Path>. However, that iterates over the names of the path, not the children of the path. In other words, this:
Path path = Path.of("foo", "bar", "file.txt");
for (Path name : path) {
System.out.println(name);
}
Will output:
foo
bar
file.txt
If you want to iterate over the children of a directory you need to use something like Files#list(Path) or Files#newDirectoryStream(Path) (the latter has two overloads). Both those methods only return the immediate children of the directory. There are other methods available to recursively iterate a directory and its sub-directories; browse the Files documentation to see what's provided.
So your code should instead look something like:
Path oldDir = Path.of(...);
Path newDir = Path.of(...);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(oldDir)) {
for (Path oldFile : dirStream) {
Path newFile = newDir.resolve(oldFile.getFileName());
Files.copy(oldFile, newFile);
}
}

Relativise introducing redundant up-directory ".."

I'm copy files from a jar to a local folder.
The jar is located at a/b.
The folder (in the jar) I'm trying to extract is located at b/c
The destination folder should be z, i.e. a/b/c/x -> z/x
When I use c.relativize("x") I get ../c/x instead of x.
This is a problem when I then try to do: z.resolve(c.relativize(x)).
I get z/../c/x instead of z/x
How do I fix this issue?
I tried z.resolve(c.relativize(x).normalise()) but get the same result
The jar is b in the folder a. c is a resource (folder) inside the jar b. x is a file inside the folder c.
The jar is in folder A/B/
The source folder path inside the jar is /b/c/ with file /b/c/xxx.yyy
The destination folder is Z
The destination file should have path: Z/xxx.yyy
So:
Path sourceZip = Paths.get("/A/B/my.zip");
URI sourceZipURI = URI.create("jar:" + sourceZip.toUri());
Path targetFolder = Paths.get("/Z");
Map<String, Object> senv = new HashMap<>();
try (FileSystem sourceZipFS = FileSystems.newFileSystem(sourceZipURI, senv, null)) {
Path folderInZip = sourceZipFS.getPath("/b/c");
Files.list(folderInZip).forEach(p -> {
Path target = targetFolder.resolve(folderInZip.relativize(p).toString());
try {
Files.createDirectories(target.getParent());
Files.copy(p, target);
} catch (IOException e) {
e.printStackTrace();
}
});
}
The main issue was the reversal of relativize; not the first time I encountered the misunderstanding. child.relativize(parent) gives a .. whereas parent.relativize(child) truncates the path.
To get the target path one needs to convert the source Path to a string, to prevent combining two different file systems.
(I did not take care of subdirectories.)
I think you have misunderstood the usage of path.relativize(Path)
According to https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Path.html#relativize(java.nio.file.Path), there is a statement as below:
Constructs a relative path between this path and a given path.
e.g.
Path pathA = Paths.get("a"); // i.e. /a or /anyDir/anySubDir/a
Path pathB = Paths.get("b"); // i.e. /b or /anyDir/anySubDir/b
pathA.relativize(pathB)
Assume the result is ../b, what does it mean?
It is actually telling you: as you are inside directory a, if you want to access directory b, you need to access by this approach cd ../b.
relativize(Path) is helping to analyzing the relative path between the parent Path and the child Path (i.e. how to access from parent Path to child Path).
Alternatively, from your question, looks like you are preferring to "construct" the path instead of "analyzing" the relative path. relativize(Path) is not the one for this purpose.
So, which one, "construct" manually or "analyze", do you want to achieve?

Java: How do I find a folder with only part of the word?

I'm trying to get back a string that contains the path to a folder.
I want to do it with just a part of the folder name.
So say I have a folder called "example-1" in "usr/share/example-1", I want to be able to get that path by matching "usr/share/ex".
Just start at the last completely given directory (usr/share/) and search for a file that starts with the rest (ex).
Therefore first separate complete from incomplete:
String input = ...
int lastDirIndex = input.lastIndexOf("/");
String lastDirText = input.substring(0, lastDirIndex);
String prefix = input.substring(lastDir + 1);
Then parse the directory lastDirText, get all its files and filter for the ones that start with prefix:
Path dir = Paths.get(lastDirText);
List<Path> matchingFiles = Files.walk(dir, 1)
.filter(path -> path.getFileName().startsWith(prefix))
.collect(Collectors.toList());
The list matchingFiles then contains all files that match the given prefix.
If you don't want to limit the search to only one depth level you may adjust the depth in the Files#walk (documentation) method or even completely leave it away for maximal depth. You can also set some FileVisitOptions in this method.

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

Categories

Resources