465

Is there a Java equivalent for System.IO.Path.Combine() in C#/.NET? Or any code to accomplish this?

This static method combines one or more strings into a path.

Matthias Braun
34.8k27 gold badges158 silver badges177 bronze badges
asked Jan 5, 2009 at 6:06
1
  • 1
    This SO question might help. Commented Jan 5, 2009 at 6:38

14 Answers 14

514

Rather than keeping everything string-based, you should use a class which is designed to represent a file system path.

If you're using Java 7 or Java 8, you should strongly consider using java.nio.file.Path; Path.resolve can be used to combine one path with another, or with a string. The Paths helper class is useful too. For example:

Path path = Paths.get("foo", "bar", "baz.txt");

If you need to cater for pre-Java-7 environments, you can use java.io.File, like this:

File baseDirectory = new File("foo");
File subDirectory = new File(baseDirectory, "bar");
File fileInDirectory = new File(subDirectory, "baz.txt");

If you want it back as a string later, you can call getPath(). Indeed, if you really wanted to mimic Path.Combine, you could just write something like:

public static String combine(String path1, String path2)
{
 File file1 = new File(path1);
 File file2 = new File(file1, path2);
 return file2.getPath();
}
Matthias Braun
34.8k27 gold badges158 silver badges177 bronze badges
answered Jan 5, 2009 at 7:29

11 Comments

Beware of absolute paths. The .NET version will return path2 (ignoring path1) if path2 is an absolute path. The Java version will drop the leading / or \ and treat it as a relative path.
@Hugo: So it wastes a whole two objects? Shocking! Looks pretty clean to me, to be honest... it keeps the logic for relative file names where it belongs, in the File class.
still, I'd argue that a constructor new File(String... pathElements) would be cleaner, possible with an added new File(File basepath, String... pathElements)
@modosansreves: Look at File.getCanonicalPath.
@jpmc26: That's only evidence of it being a pain in a specific setting. I've found it very useful in Java for a variety of other needs which don't require the value as a string.
|
198

In Java 7, you should use resolve:

Path newPath = path.resolve(childPath);

While the NIO2 Path class may seem a bit redundant to File with an unnecessarily different API, it is in fact subtly more elegant and robust.

Note that Paths.get() (as suggested by someone else) doesn't have an overload taking a Path, and doing Paths.get(path.toString(), childPath) is NOT the same thing as resolve(). From the Paths.get() docs:

Note that while this method is very convenient, using it will imply an assumed reference to the default FileSystem and limit the utility of the calling code. Hence it should not be used in library code intended for flexible reuse. A more flexible alternative is to use an existing Path instance as an anchor, such as:

Path dir = ...
Path path = dir.resolve("file");

The sister function to resolve is the excellent relativize:

Path childPath = path.relativize(newPath);
answered Nov 19, 2013 at 10:50

4 Comments

"Note that while this method is very convenient, using it will imply an assumed reference to the default FileSystem and limit the utility of the calling code." Sure, but you have to use some equivalent function to generate a Path object (because it's an interface and can't be instantiated directly). So at some point you are creating a reference to the underlying FS. I think the docs are misleading here.
@éclairevoyant They mean the reference will be to the default FS, rather than the FS that the parent path refers to. Unlike most OSes, Java supports multiple, separate filesystems. Eg, a zip file or cloud storage can be exposed as a FS object. It’s a cool, although not-widely-used-yet feature.
All modern OSes should support multiple filesystems, but that's besides the point. If not Paths.get(...) how would one create a Path without using an existing Path? (Maybe this should be a new question.)
@éclairevoyant FileSystem::getPath I should have said Java supports multiple FS namespaces (one per FS object). OSes have a single namespace.
47

The main answer is to use File objects. However Commons IO does have a class FilenameUtils that can do this kind of thing, such as the concat() method.

answered Jan 5, 2009 at 13:05

2 Comments

If youre working with something like JSF, youre definitly want to keep it String-based as all the Paths you 'll get will be String-based.
33

platform independent approach (uses File.separator, ie will works depends on operation system where code is running:

java.nio.file.Paths.get(".", "path", "to", "file.txt")
// relative unix path: ./path/to/file.txt
// relative windows path: .\path\to\filee.txt
java.nio.file.Paths.get("/", "path", "to", "file.txt")
// absolute unix path: /path/to/filee.txt
// windows network drive path: \\path\to\file.txt
java.nio.file.Paths.get("C:", "path", "to", "file.txt")
// absolute windows path: C:\path\to\file.txt
answered Aug 9, 2017 at 14:43

Comments

18

I know its a long time since Jon's original answer, but I had a similar requirement to the OP.

By way of extending Jon's solution I came up with the following, which will take one or more path segments takes as many path segments that you can throw at it.

Usage

Path.combine("/Users/beardtwizzle/");
Path.combine("/", "Users", "beardtwizzle");
Path.combine(new String[] { "/", "Users", "beardtwizzle", "arrayUsage" });

Code here for others with a similar problem

public class Path {
 public static String combine(String... paths)
 {
 File file = new File(paths[0]);
 for (int i = 1; i < paths.length ; i++) {
 file = new File(file, paths[i]);
 }
 return file.getPath();
 }
}
answered Feb 5, 2012 at 9:31

Comments

13

To enhance JodaStephen's answer, Apache Commons IO has FilenameUtils which does this. Example (on Linux):

assert org.apache.commons.io.FilenameUtils.concat("/home/bob", "work\\stuff.log") == "/home/bob/work/stuff.log"

It's platform independent and will produce whatever separators your system needs.

answered Nov 14, 2010 at 13:31

Comments

6

Late to the party perhaps, but I wanted to share my take on this. I prefer not to pull in entire libraries for something like this. Instead, I'm using a Builder pattern and allow conveniently chained append(more) calls. It even allows mixing File and String, and can easily be extended to support Path as well. Furthermore, it automatically handles the different path separators correctly on both Linux, Macintosh, etc.

public class Files {
 public static class PathBuilder {
 private File file;
 private PathBuilder ( File root ) {
 file = root;
 }
 private PathBuilder ( String root ) {
 file = new File(root);
 }
 public PathBuilder append ( File more ) {
 file = new File(file, more.getPath()) );
 return this;
 }
 public PathBuilder append ( String more ) {
 file = new File(file, more);
 return this;
 }
 public File buildFile () {
 return file;
 }
 }
 public static PathBuilder buildPath ( File root ) {
 return new PathBuilder(root);
 }
 public static PathBuilder buildPath ( String root ) {
 return new PathBuilder(root);
 }
}

Example of usage:

File root = File.listRoots()[0];
String hello = "hello";
String world = "world";
String filename = "warez.lha"; 
File file = Files.buildPath(root).append(hello).append(world)
 .append(filename).buildFile();
String absolute = file.getAbsolutePath();

The resulting absolute will contain something like:

/hello/world/warez.lha

or maybe even:

A:\hello\world\warez.lha
answered Feb 12, 2019 at 10:54

2 Comments

JDK22 gives easier to use Path.of("/").resolve("hello", "world", "warez.lha"). Btw Files is not a good choice of name as it hides the nio version.
Looks like someone got inspired by my Files class. And you're right, I should probably rename it to AwesomeFiles. Oh, and thanks for the update and example on JDK22 Path =)
4

If you do not need more than strings, you can use com.google.common.io.Files

Files.simplifyPath("some/prefix/with//extra///slashes" + "file//name")

to get

"some/prefix/with/extra/slashes/file/name"
answered Apr 3, 2017 at 12:44

Comments

2

Here's a solution which handles multiple path parts and edge conditions:

public static String combinePaths(String ... paths)
{
 if ( paths.length == 0)
 {
 return "";
 }
 File combined = new File(paths[0]);
 int i = 1;
 while ( i < paths.length)
 {
 combined = new File(combined, paths[i]);
 ++i;
 }
 return combined.getPath();
}
bluish
27.5k28 gold badges125 silver badges185 bronze badges
answered Dec 7, 2010 at 18:36

Comments

1

This also works in Java 8 :

Path file = Paths.get("Some path");
file = Paths.get(file + "Some other path");
answered Jun 16, 2019 at 2:26

1 Comment

This concatenates the filenames, combine would resolve the sub-directory as "Some path/Some other path"
1

Java 8

For combining multiple paths, there is an NIO package with Path#resolve(String):

Path path = Path.of("/Users/user/Projects");
path = path.resolve("work")
 .resolve("bonk-app")
 .resolve("docs");

/Users/user/Projects/work/bonk-app/docs

If you have the subpaths to be resolved in an array or any collection, you can use Stream#reduce:

Path path = Path.of("/Users/user/Projects");
List<String> subpaths = Arrays.asList("work", "bonk-app", "docs");
 
path = subpaths.stream()
 .reduce(path, Path::resolve, (l,r) -> r);

/Users/user/Projects/work/bonk-app/docs

Java 22

Java 22 offers two methods Path#resolve(String, String...) and Path#resolve(Path, Path...). At the first glance, they look useful, though it depends on the source of subpaths.

Path path = Path.of("/Users/user/Projects");
path = path.resolve("work", "bonk-app", "docs");

/Users/user/Projects/work/bonk-app/docs

Anyway, it is good to know there is this option.

answered Feb 12 at 5:55

Comments

0

This solution offers an interface for joining path fragments from a String[] array. It uses java.io.File.File(String parent, String child):

 public static joinPaths(String[] fragments) {
 String emptyPath = "";
 return buildPath(emptyPath, fragments);
 }
 private static buildPath(String path, String[] fragments) {
 if (path == null || path.isEmpty()) {
 path = "";
 }
 if (fragments == null || fragments.length == 0) {
 return "";
 }
 int pathCurrentSize = path.split("/").length;
 int fragmentsLen = fragments.length;
 if (pathCurrentSize <= fragmentsLen) {
 String newPath = new File(path, fragments[pathCurrentSize - 1]).toString();
 path = buildPath(newPath, fragments);
 }
 return path;
 }

Then you can just do:

String[] fragments = {"dir", "anotherDir/", "/filename.txt"};
String path = joinPaths(fragments);

Returns:

"/dir/anotherDir/filename.txt"
answered Oct 4, 2019 at 15:23

Comments

0

Assuming all given paths are absolute paths. you can follow below snippets to merge these paths.

String baseURL = "\\\\host\\testdir\\";
String absoluteFilePath = "\\\\host\\testdir\\Test.txt";;
String mergedPath = Paths.get(baseURL, absoluteFilePath.replaceAll(Matcher.quoteReplacement(baseURL), "")).toString();

output path is \\host\testdir\Test.txt.

answered Jun 8, 2021 at 13:14

Comments

0
 public static Path resolve(Path base, String... elements) {
 return Arrays.stream(elements).map(Path::of).reduce(base, Path::resolve);
 }
answered Jun 25, 2024 at 10:55

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.