2008-09-09 83 views
140

从Visual Studio中的即时窗口Path.Combine为什么不正确连接以Path.DirectorySeparatorChar开头的文件名?

> Path.Combine(@"C:\x", "y") 
"C:\\x\\y" 
> Path.Combine(@"C:\x", @"\y") 
"\\y" 

他们似乎都应该是相同的。

老FileSystemObject.BuildPath()没有以这种方式工作...

+40

OMG这太愚蠢了,它“工作”这种方式。 – Joe 2011-08-19 18:30:03

+0

[它仍然不会在.NET核心中改变。](https://github.com/dotnet/coreclr/blob/fb86c0294a999b2c7bd1e13da1fdc0d3c2f701e5/src/mscorlib/shared/System/IO/Path.cs#L189) – zwcloud 2017-04-12 15:43:40

+0

@Joe ,愚蠢是对的!另外,我必须指出[等效函数](https://nodejs.org/api/path.html#path_path_join_paths)在Node.JS中工作得很好......在微软上摇头...... – 2017-09-06 18:19:42

回答

158

这是一个哲学问题(可能只有微软才能真正回答),因为它正在完成文档所说的内容。

System.IO.Path.Combine

“如果PATH2包含一个绝对路径,这个方法返回路径2”。

Here's the actual Combine method from .NET source。你可以看到它调用CombineNoChecks,然后在路径2上调用IsPathRooted,如果是,则返回该路径。

我不知道理由是什么。我想解决方案是从第二条路径的开始剥离(或修剪)DirectorySeparatorChar;也许写你自己的Combine方法,然后调用Path.Combine()。

+0

反汇编代码(查看我的文章),你是对的。 – 2008-09-10 00:25:32

5

不知道实际的细节,我的猜测是,它使一个尝试加入像你可能会加入相关的URI。例如:

urljoin('/some/abs/path', '../other') = '/some/abs/other' 

这意味着,当你加入一个路径与前面的斜线时,实际上是连接一个基站到另一个,在此情况下,第二获取优先级。

6

MSDN

如果指定的路径中的一个是一个零长度字符串,则此方法返回其他路径。如果path2包含绝对路径,则此方法返回path2。

在你的例子中,path2是绝对的。

22

这是Path.Combine方法的.NET Reflector的反汇编代码。检查IsPathRooted函数。如果第二个路径是根(以DirectorySeparatorChar开头),则返回第二个路径。

public static string Combine(string path1, string path2) 
{ 
    if ((path1 == null) || (path2 == null)) 
    { 
     throw new ArgumentNullException((path1 == null) ? "path1" : "path2"); 
    } 
    CheckInvalidPathChars(path1); 
    CheckInvalidPathChars(path2); 
    if (path2.Length == 0) 
    { 
     return path1; 
    } 
    if (path1.Length == 0) 
    { 
     return path2; 
    } 
    if (IsPathRooted(path2)) 
    { 
     return path2; 
    } 
    char ch = path1[path1.Length - 1]; 
    if (((ch != DirectorySeparatorChar) && 
     (ch != AltDirectorySeparatorChar)) && 
     (ch != VolumeSeparatorChar)) 
    { 
     return (path1 + DirectorySeparatorChar + path2); 
    } 
    return (path1 + path2); 
} 


public static bool IsPathRooted(string path) 
{ 
    if (path != null) 
    { 
     CheckInvalidPathChars(path); 
     int length = path.Length; 
     if (
       (
        (length >= 1) && 
        (
         (path[0] == DirectorySeparatorChar) || 
         (path[0] == AltDirectorySeparatorChar) 
       ) 
      ) 

       || 

       ((length >= 2) && 
       (path[1] == VolumeSeparatorChar)) 
      ) 
     { 
      return true; 
     } 
    } 
    return false; 
} 
14

在我看来,这是一个错误。问题是有两种不同类型的“绝对”路径。路径“d:\ mydir \ myfile.txt”是绝对路径,“\ mydir \ myfile.txt”路径也被认为是“绝对”,即使它缺少驱动器号。在我看来,正确的行为将是当第二个路径以目录分隔符开始(并且不是UNC路径)时,从第一个路径预先安装驱动器号。我会建议编写你自己的帮助包装函数,如果你需要它有你想要的行为。

0

此\表示“当前驱动器的根目录”。在您的示例中,它表示当前驱动器根目录中的“test”文件夹。如果不想失去你可以利用这个任意路径结合两种路径

0

?Path.Combine(@"C:\test", @"\test".Substring(0, 1) == @"\" ? @"\test".Substring(1, @"\test".Length - 1) : @"\test"); 

或用变量:

string Path1 = @"C:\Test"; 
string Path2 = @"\test"; 
string FullPath = Path.Combine(Path1, Path2.Substring(0, 1) == @"\" ? Path2.Substring(1, Path2.Length - 1) : Path2); 

:那么,这可以用“\测试C”等于这两种情况都会返回“C:\ test \ test”。

首先,我评估Path2是否以/开头,如果是,则返回没有第一个字符的Path2。否则,返回完整路径2。

3

此代码应该做的伎俩:

 string strFinalPath = string.Empty; 
     string normalizedFirstPath = Path1.TrimEnd(new char[] { '\\' }); 
     string normalizedSecondPath = Path2.TrimStart(new char[] { '\\' }); 
     strFinalPath = Path.Combine(normalizedFirstPath, normalizedSecondPath); 
     return strFinalPath; 
13

好了,已经很长的答案列表,这里是我的;-)

我想解决这个问题:

string sample1 = "configuration/config.xml"; 
string sample2 = "/configuration/config.xml"; 
string sample3 = "\\configuration/config.xml"; 

string dir1 = "c:\\temp"; 
string dir2 = "c:\\temp\\"; 
string dir3 = "c:\\temp/"; 

string path1 = PathCombine(dir1, sample1); 
string path2 = PathCombine(dir1, sample2); 
string path3 = PathCombine(dir1, sample3); 

string path4 = PathCombine(dir2, sample1); 
string path5 = PathCombine(dir2, sample2); 
string path6 = PathCombine(dir2, sample3); 

string path7 = PathCombine(dir3, sample1); 
string path8 = PathCombine(dir3, sample2); 
string path9 = PathCombine(dir3, sample3); 

当然,所有程序1-9最后都应该包含一个等效的字符串。这里是我想出了PathCombine方法:

private string PathCombine(string path1, string path2) 
{ 
    if (Path.IsPathRooted(path2)) 
    { 
     path2 = path2.TrimStart(Path.DirectorySeparatorChar); 
     path2 = path2.TrimStart(Path.AltDirectorySeparatorChar); 
    } 

    return Path.Combine(path1, path2); 
} 

我也觉得这是很烦人的,这个字符串处理都必须手工完成,我很想在这背后的原因。

0

这实际上是有道理的,在某些方面,考虑如何(相对)路径通常处理:

string GetFullPath(string path) 
{ 
    string baseDir = @"C:\Users\Foo.Bar"; 
    return Path.Combine(baseDir, path); 
} 

// get full path for RELATIVE file path 
GetFullPath("file.txt"); // = C:\Users\Foo.Bar\file.txt 

// get full path for ROOTED file path 
GetFullPath(@"C:\Temp\file.txt"); // = C:\Temp\file.txt 

真正的问题是,为什么与"\"作为被认为是“根”开始的路径。这是新的给我,但it works that way on windows

new FileInfo("\windows"); // FullName = C:\Windows, Exists = True 
new FileInfo("windows"); // FullName = C:\Users\Foo.Bar\Windows, Exists = False 
4

在他的‘事情我讨厌微软’继Christian Graus的意见博客名为‘Path.Combine is essentially useless.’,这里是我的解决方案:

public static class Pathy 
{ 
    public static string Combine(string path1, string path2) 
    { 
     if (path1 == null) return path2 
     else if (path2 == null) return path1 
     else return path1.Trim().TrimEnd(System.IO.Path.DirectorySeparatorChar) 
      + System.IO.Path.DirectorySeparatorChar 
      + path2.Trim().TrimStart(System.IO.Path.DirectorySeparatorChar); 
    } 

    public static string Combine(string path1, string path2, string path3) 
    { 
     return Combine(Combine(path1, path2), path3); 
    } 
} 

一些建议那名字空间应该碰撞,...我用Pathy作为轻微的去,并且避免与System.IO.Path的命名空间冲突。

编辑:加空参数检查

0

这两种方法应该不小心连接两个字符串,都在他们的分隔符救你。

public static string Combine(string x, string y, char delimiter) { 
     return $"{ x.TrimEnd(delimiter) }{ delimiter }{ y.TrimStart(delimiter) }"; 
    } 

    public static string Combine(string[] xs, char delimiter) { 
     if (xs.Length < 1) return string.Empty; 
     if (xs.Length == 1) return xs[0]; 
     var x = Combine(xs[0], xs[1], delimiter); 
     if (xs.Length == 2) return x; 
     var ys = new List<string>(); 
     ys.Add(x); 
     ys.AddRange(xs.Skip(2).ToList()); 
     return Combine(ys.ToArray(), delimiter); 
    } 
相关问题