In C#, how do I combine more than two parts of a file path at once?
Asked Answered
A

5

34

To combine two parts of a file path, you can do

System.IO.Path.Combine (path1, path2);

However, you can't do

System.IO.Path.Combine (path1, path2, path3);

Is there a simple way to do this?

Aglimmer answered 3/1, 2010 at 20:15 Comment(0)
P
29

As others have said, in .NET 3.5 and earlier versions there hasn't been a way to do this neatly - you either have to write your own Combine method or call Path.Combine multiple times.

But rejoice - for in .NET 4.0, there is this overload:

public static string Combine(
    params string[] paths
)

There are also overloads taking 3 or 4 strings, presumably so that it doesn't need to create an array unnecessarily for common cases.

Hopefully Mono will port those overloads soon - I'm sure they'd be easy to implement and much appreciated.

Pantomimist answered 3/1, 2010 at 20:34 Comment(5)
Shouldn't declaring single array be more efficient then declaring 4 different variables? What could be the reason for 4 or 3 string overload?Beer
@Hasan: No, creating an array requires a separate object which has to be garbage collected later, etc. Passing two separate variables is more efficient than creating a new array containing two references.Pantomimist
No need to wait, it's implemented since 10/21/09 :) anonsvn.mono-project.com/…Predominant
msdn.microsoft.com/en-us/library/dd784047(VS.90).aspx states that Path.Combine will take 3 strings in .Net 3.5 which is clearly wrong since it gives a compiler error when used. the msdn library is wrong in this case. I put this here in case someone else lands on this page after getting this compiler error.Adamson
Now that .NET 4.0 is out, I have changed the accepted answer to this answer.Aglimmer
I
35

Here's a utility method you can use:

public static string CombinePaths(string path1, params string[] paths)
{
    if (path1 == null)
    {
        throw new ArgumentNullException("path1");
    }
    if (paths == null)
    {
        throw new ArgumentNullException("paths");
    }
    return paths.Aggregate(path1, (acc, p) => Path.Combine(acc, p));
}

Alternate code-golf version (shorter, but not quite as clear, semantics are a bit different from Path.Combine):

public static string CombinePaths(params string[] paths)
{
    if (paths == null)
    {
        throw new ArgumentNullException("paths");
    }
    return paths.Aggregate(Path.Combine);
}

Then you can call this as:

string path = CombinePaths(path1, path2, path3);
Imp answered 3/1, 2010 at 20:20 Comment(7)
This might be a stupid question, but where does Aggregate come from? I'm using Mono targeting Mono / .Net 3.5, and the compiler can't find it.Aglimmer
Ahh - Aggregate is an extension method in the System.Linq namespace, which is in System.Core.dll.Imp
Why the extra 'path1' parameter? Also, the last line can be shortened to return paths.Aggregate(/*path1, */Path.Combine);Predominant
@Kha: This is true, I wrote it that way to preserve the semantics of the original Path.Combine and to try to indicate clearly that at least one path is required for the method to work correctly. Path.Combine will throw an ArgumentNullException if either path1 or path2 are null. But you're absolutely right, it's not totally necessary.Imp
Kha: I like that simple version better. I edited my question to include the modified utility method.Aglimmer
Resharper is so nice to suggest: "Use Method Group" (second return statement vesion)Papke
I would just like to add, and this is an issue with Path.Combine, not this code, that adding a leading "\" on a non-starting parameter will ignore the previous.Expiatory
P
29

As others have said, in .NET 3.5 and earlier versions there hasn't been a way to do this neatly - you either have to write your own Combine method or call Path.Combine multiple times.

But rejoice - for in .NET 4.0, there is this overload:

public static string Combine(
    params string[] paths
)

There are also overloads taking 3 or 4 strings, presumably so that it doesn't need to create an array unnecessarily for common cases.

Hopefully Mono will port those overloads soon - I'm sure they'd be easy to implement and much appreciated.

Pantomimist answered 3/1, 2010 at 20:34 Comment(5)
Shouldn't declaring single array be more efficient then declaring 4 different variables? What could be the reason for 4 or 3 string overload?Beer
@Hasan: No, creating an array requires a separate object which has to be garbage collected later, etc. Passing two separate variables is more efficient than creating a new array containing two references.Pantomimist
No need to wait, it's implemented since 10/21/09 :) anonsvn.mono-project.com/…Predominant
msdn.microsoft.com/en-us/library/dd784047(VS.90).aspx states that Path.Combine will take 3 strings in .Net 3.5 which is clearly wrong since it gives a compiler error when used. the msdn library is wrong in this case. I put this here in case someone else lands on this page after getting this compiler error.Adamson
Now that .NET 4.0 is out, I have changed the accepted answer to this answer.Aglimmer
P
5

Not simple, but clever :)

string str1 = "aaa", str2 = "bbb", str3 = "ccc";
string comb = new string[] { str1, str2, str3 }
    .Aggregate((x, y) => System.IO.Path.Combine(x, y));

Or:

string CombinePaths(params string[] paths)
{
    return paths.Aggregate((x,y) => System.IO.Path.Combine(x, y));
}

EDIT Order23's answer is actually up to date with current .NET https://mcmap.net/q/35542/-in-c-how-do-i-combine-more-than-two-parts-of-a-file-path-at-once

Palma answered 3/1, 2010 at 20:20 Comment(1)
This is handy if you're not doing it more than once. You can simplify it a lot, though: new [] { "aaa", "bbb", "ccc" }.Aggregate (Path.Combine); (assuming you are using System.IO;).Aglimmer
D
4

Nope - you have to call Path.Combine() several times.

You could write a helper method that does it for you, though:

public static string CombinePaths(params string[] paths) {
    if (paths == null) {
        return null;
    }
    string currentPath = paths[0];
    for (int i = 1; i < paths.Length; i++) {
        currentPath = Path.Combine(currentPath, paths[i]);
    }
    return currentPath;
}
Deneendenegation answered 3/1, 2010 at 20:17 Comment(6)
Taking a look at the logic you can easily get rid of that second if statement.Prim
@ChaosPandion: If you're going to avoid Linq then you should also optimize the Schlemiel-the-painter algorithm and instead use Path.PathSeparator and other static fields with a StringBuilder. I figured performance wasn't an issue here.Imp
"Unnecessary LINQ"? Perhaps you forgot to specify the [.net-2.0] tag?Hyacinthus
@Prim - "unnecessary Linq"? Maybe - if performance was critical. Or perhaps if only .Net 2. But actually the Linq approach is simple, concise and intentional. And if it gets people to think in functional terms it is no bad thing.Crenelate
I love LINQ as much as the next guy but come on for something as simple as this? You guys need to take it easy.Prim
string strPath = ""; foreach (string str in paths) strPath = System.IO.Path.Combine(strPath, str); will do. You needn't take care of path 0.Aplite
L
2

With the method overload introduced in .NET 4 Path.Combine(string [])

Path.Combine(new [] { "abc", "def", "ghi", "jkl", "mno" });
Louvain answered 14/12, 2016 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.