2008-09-09 85 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); 
    } 
相關問題