2008-09-09 21 views
140

Từ Window ngay trong Visual Studio:Tại sao Path.Combine không ghép nối đúng tên tệp bắt đầu bằng Path.DirectorySeparatorChar?

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

Dường như họ nên cả hai là như nhau.

Các FileSystemObject.BuildPath cũ() không làm việc theo cách này ...

+40

OMG điều này thật ngu ngốc đến nỗi nó "hoạt động" theo cách này. – Joe

+0

[Nó vẫn không thay đổi trong lõi .NET.] (Https://github.com/dotnet/coreclr/blob/fb86c0294a999b2c7bd1e13da1fdc0d3c2f701e5/src/mscorlib/shared/System/IO/Path.cs#L189) – zwcloud

+0

@Joe , ngu ngốc là đúng! Ngoài ra, tôi phải chỉ ra rằng [chức năng tương đương] (https://nodejs.org/api/path.html#path_path_join_paths) hoạt động tốt trong Node.JS ... Lắc đầu với Microsoft ... –

Trả lời

158

Đây là một câu hỏi triết học (mà có lẽ chỉ có Microsoft mới có thể trả lời), vì nó thực hiện chính xác những gì tài liệu nói.

System.IO.Path.Combine

"Nếu path2 chứa một đường dẫn tuyệt đối, phương thức này trả path2."

Here's the actual Combine method từ nguồn .NET. Bạn có thể thấy rằng nó gọi CombineNoChecks, sau đó gọi IsPathRooted trên đường dẫn 2 và trả về đường dẫn đó nếu có.

Tôi không biết lý do là gì. Tôi đoán giải pháp là để tắt (hoặc Trim) DirectorySeparatorChar từ đầu đường dẫn thứ hai; có thể viết phương thức Combine của riêng bạn thực hiện điều đó và sau đó gọi Path.Combine().

+0

Nhìn vào mã tháo rời (kiểm tra bài đăng của tôi), bạn đúng theo cách. –

+6

Tôi đoán nó hoạt động theo cách đó để cho phép truy cập dễ dàng vào thuật toán "dir làm việc hiện tại". – BCS

+0

Dường như nó hoạt động như một chuỗi 'cd (thành phần)' từ dòng lệnh. Nghe có vẻ hợp lý với tôi. –

5

Không biết các chi tiết thực tế, tôi đoán là nó làm cho một nỗ lực để tham gia như bạn có thể tham gia các URI tương đối. Ví dụ:

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

Điều này có nghĩa là khi bạn tham gia một đường dẫn với dấu gạch chéo trước, bạn sẽ tham gia một cơ sở với dấu gạch chéo trước, trong trường hợp thứ hai được ưu tiên.

6

Từ MSDN:

Nếu một trong những con đường được quy định là một chuỗi số không dài, phương pháp này trả về con đường khác. Nếu path2 chứa đường dẫn tuyệt đối, phương thức này trả về đường dẫn 2.

Trong ví dụ của bạn, đường dẫn 2 là tuyệt đối.

22

Đây là mã chưa được tách rời từ .NET Reflector cho phương pháp Path.Combine. Kiểm tra hàm IsPathRooted. Nếu đường dẫn thứ hai được bắt nguồn từ (bắt đầu bằng một DirectorySeparatorChar), hãy trả lại đường dẫn thứ hai.

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

Theo tôi đây là lỗi. Vấn đề là có hai loại đường dẫn "tuyệt đối" khác nhau. Đường dẫn "d: \ mydir \ myfile.txt" là tuyệt đối, đường dẫn "\ mydir \ myfile.txt" cũng được coi là "tuyệt đối" ngay cả khi nó thiếu ký tự ổ đĩa. Các hành vi chính xác, theo ý kiến ​​của tôi, sẽ là để thêm chữ cái ổ đĩa từ đường dẫn đầu tiên khi đường dẫn thứ hai bắt đầu với phân cách thư mục (và không phải là một đường dẫn UNC). Tôi sẽ khuyên bạn nên viết hàm helper wrapper của riêng bạn mà có hành vi mà bạn mong muốn nếu bạn cần nó.

+1

Nó phù hợp với spec, nhưng nó không phải là những gì tôi mong đợi. – dthrasher

+0

@Jake Đó không phải là tránh một bugfix; đó là một số người suy nghĩ lâu dài và khó khăn về cách làm một cái gì đó, và sau đó gắn bó với bất cứ điều gì họ đồng ý. Ngoài ra, lưu ý sự khác biệt giữa khung .Net (một thư viện chứa 'Path.Combine') và ngôn ngữ C#. – Grault

0

Điều này có nghĩa là "thư mục gốc của ổ đĩa hiện tại". Trong ví dụ của bạn, nó có nghĩa là thư mục "test" trong thư mục gốc của ổ đĩa hiện tại.Vì vậy, điều này có thể tương đương với "c: \ test"

0

Nếu bạn muốn kết hợp cả hai con đường mà không bị mất bất kỳ đường dẫn mà bạn có thể sử dụng này:

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

Hoặc với các biến:

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

Cả hai trường hợp đều trả về "C: \ test \ test".

Đầu tiên, tôi đánh giá nếu Path2 bắt đầu bằng/và nếu đúng, hãy trả về đường dẫn 2 mà không có ký tự đầu tiên. Nếu không, hãy trả về đầy đủ Path2.

+0

Nó có thể an toàn hơn để thay thế '== @" \ "' kiểm tra bằng một cuộc gọi 'Path.IsRooted()' vì '" \ "' không phải là ký tự duy nhất để giải thích. – rumblefx0

3

Mã này nên làm như lừa:

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

Ok, đã là một danh sách dài các câu trả lời, đây là của tôi ;-)

tôi muốn giải quyết vấn đề này:

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

Tất nhiên, tất cả các đường 1-9 phải chứa một chuỗi tương đương ở cuối. Dưới đây là phương pháp PathCombine tôi đến với:

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

Tôi cũng nghĩ rằng nó là khá khó chịu mà xử lý chuỗi này phải được thực hiện bằng tay, tôi muốn được quan tâm đến lý do đằng sau này.

0

Điều này thực sự có ý nghĩa, một cách nào đó, xem xét như thế nào (tương đối) con đường được điều trị thường:

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 

Câu hỏi thực sự là, tại sao con đường mà bắt đầu với "\" như coi là "bắt nguồn". Đây là mới đối với tôi, nhưng 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

Sau Christian Graus 'lời khuyên trong "Things I Hate About Microsoft" của mình trên blog mang tên "Path.Combine is essentially useless.", đây là giải pháp của tôi:

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

Một số lời khuyên rằng các không gian tên nên va chạm, ... Tôi đã đi với Pathy, như một chút, và để tránh va chạm không gian tên với System.IO.Path.

Sửa: Thêm kiểm tra tham số rỗng

0

Hai phương pháp này cần lưu bạn vô tình tham gia vào hai chuỗi mà cả hai đều có dấu phân cách trong đó.

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); 
    } 
Các vấn đề liên quan