Path.Combine neden Path.DirectorySeparatorChar ile başlayan dosya adlarını düzgün şekilde birleştirmiyor?


186

Gönderen komut penceresinde Visual Studio:

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

Her ikisinin de aynı olması gerektiği anlaşılıyor.

Eski FileSystemObject.BuildPath () bu şekilde çalışmadı ...



@Joe, aptal haklı! Ayrıca, eşdeğer işlevin Node.JS'de gayet iyi çalıştığını da belirtmeliyim ... Microsoft'ta başımı sallıyorum ...
NH.

2
@zwcloud .NET Core / Standard için, Path.Combine()temel olarak geriye dönük uyumluluk içindir (mevcut davranışla). Daha iyi kullanarak atılırım Path.Join(): "yöntemini birleştirin aksine, yöntem köküne döndü yolunu denemez Üyelik (path2, yöntemini Üyelik mutlak bir yol değil ıskarta path1 ve dönüş path2 birleştirin olarak gelmez ise bu vardır. ")
Stajs

Yanıtlar:


205

Bu bir tür felsefi sorudur (belki de sadece Microsoft gerçekten cevap verebilir), çünkü belgelerin tam olarak söylediklerini yapıyor.

System.IO.Path.Combine

"Path2 mutlak bir yol içeriyorsa, bu yöntem path2'yi döndürür."

İşte .NET kaynağından gerçek Kombine yöntemi . CombineNoChecks'i çağırdığını görebilirsiniz; bu yol daha sonra path2'de IsPathRooted öğesini çağırır ve varsa bu yolu döndürür:

public static String Combine(String path1, String path2) {
    if (path1==null || path2==null)
        throw new ArgumentNullException((path1==null) ? "path1" : "path2");
    Contract.EndContractBlock();
    CheckInvalidPathChars(path1);
    CheckInvalidPathChars(path2);

    return CombineNoChecks(path1, path2);
}

internal static string CombineNoChecks(string path1, string 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 + DirectorySeparatorCharAsString + path2;
    return path1 + path2;
}

Gerekçenin ne olduğunu bilmiyorum. Sanırım çözüm ikinci yolun başından itibaren DirectorySeparatorChar çıkarmak (veya Trim) olduğunu; belki bunu yapan ve sonra Path.Combine () çağıran kendi Kombine yönteminizi yazın.


Demonte koda baktığımda (yazımı kontrol et) bir bakıma haklısın.
Gulzar Nazim

7
"Geçerli çalışma dir" algoritmasına kolay erişim sağlamak için bu şekilde çalışır sanırım.
BCS

cd (component)Komut satırından bir dizi yapmak gibi görünüyor . Bana mantıklı geliyor.
Adrian Ratnapala

11
İstediğiniz efekt dizesini almak için bu trim kullanın strFilePath = Path.Combine (basePath, otherPath.TrimStart (new char [] {'\\', '/'}));
Matthew Lock

3
Ben çalışma kodumu Path.Combinesadece güvenli olmak için değiştirdim ama sonra kırdı .. Çok aptal :)
sotn

23

Bu Path.Combine yöntemi için .NET Reflector demonte koddur . IsPathRooted işlevini kontrol edin. İkinci yol köklü ise (bir DirectorySeparatorChar ile başlar), ikinci yolu olduğu gibi döndürün.

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

23

Bu sorunu çözmek istedim:

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

Elbette, 1-9 arasındaki tüm yollar sonunda eşdeğer bir dize içermelidir. İşte geldim PathCombine yöntemi:

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

Ayrıca bu dize işleme elle yapılması gerektiğini oldukça sinir bozucu olduğunu düşünüyorum ve bunun arkasındaki neden ilgi olacaktır.


19

Bence bu bir hatadır. Sorun iki farklı tür "mutlak" yol olmasıdır. "D: \ mydir \ myfile.txt" yolu mutlaktır, "\ mydir \ myfile.txt" yolu da sürücü harfini içermese de "mutlak" kabul edilir. Bence doğru davranış, ikinci yol dizin ayırıcıyla (ve bir UNC yolu değil) başladığında sürücü harfini ilk yolun başına eklemek olacaktır. İhtiyacınız olursa istediğiniz davranışa sahip kendi yardımcı sarmalayıcı işlevinizi yazmanızı öneririm.


7
Spesifikasyonla eşleşir, ancak ben de beklemiyordum.
dthrasher

@Jake Bu bir hata düzeltmesini engellemez; Bu, bir şeyi nasıl yapacağını uzun ve zor düşünen ve sonra üzerinde anlaştıkları her şeye bağlı kalan birkaç insan. Ayrıca, .Net çerçevesi (içeren bir kitaplık Path.Combine) ile C # dili arasındaki farka dikkat edin .
16'da

9

Gönderen MSDN :

Belirtilen yollardan biri sıfır uzunluklu bir dizeyse, bu yöntem diğer yolu döndürür. Path2 mutlak bir yol içeriyorsa, bu yöntem path2'yi döndürür.

Örneğinizde, path2 mutlaktır.


7

Christian Graus'un " Path.Combine aslında işe yaramaz. " Başlıklı "Microsoft Hakkında Şeylerden Nefret Ediyorum" blogundaki tavsiyelerini takiben , benim çözümüm:

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

Bazıları isim alanlarının çarpışmasını tavsiye ediyor, ... Ben Pathyhafifçe gittim ve isim-alanı çarpışmasını önlemek için gittim System.IO.Path.

Düzenleme : Boş parametre kontrolleri eklendi


4

Bu kod hile yapmalıdır:

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

4

Gerçek ayrıntıları bilmeden, tahminim göreli URI'lara katılabileceğiniz gibi katılma girişiminde bulunmasıdır. Örneğin:

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

Bu, bir eğik çizgiyle bir yola katıldığınızda, aslında bir üsse diğerine katılırsınız, bu durumda ikincisi öncelik kazanır.


Bence öne eğik çizgiler açıklanmalıdır. Ayrıca, bunun .NET ile ne ilgisi var?
Peter Mortensen

3

Sebep:

İkinci URL'niz mutlak bir yol olarak kabul edilir. CombineYöntem yalnızca son yol mutlak bir yolsa son yolu döndürür.

Çözüm: Sadece /ikinci Yolunuzun ( /SecondPath- SecondPath) başlangıç ​​eğik çizgisini kaldırın . Sonra istisna olarak çalışır.


3

Bu, aslında (göreli) yolların genellikle nasıl ele alındığını göz önünde bulundurarak bir şekilde mantıklıdır:

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

Asıl soru şudur: Neden başlayan yollar "\""köklü" kabul edilir? Bu benim için de yeni bir şeydi, ancak Windows'da şu şekilde çalışıyor :

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

1

Herhangi bir yolu kaybetmeden her iki yolu birleştirmek istiyorsanız, bunu kullanabilirsiniz:

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

Veya değişkenlerle:

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

Her iki durumda da "C: \ test \ test" döndürülür.

İlk olarak, Path2'nin / ile başlayıp başlamadığını değerlendiririm ve doğruysa, ilk karakteri olmadan Path2'yi döndürürüm. Aksi takdirde, Path2'nin tamamını döndürün.


1
Muhasebenin tek karakteri olmadığından, == @"\"çek ile bir Path.IsRooted()çağrıyı değiştirmek muhtemelen daha güvenlidir "\".
rumblefx0

0

Bu iki yöntem, ikisinde de sınırlayıcı bulunan iki dizeyi yanlışlıkla birleştirmekten kurtarmalıdır.

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

0

Bu \ "geçerli sürücünün kök dizini" anlamına gelir. Örneğin, geçerli sürücünün kök dizinindeki "test" klasörü anlamına gelir. Yani, bu "c: \ test" e eşit olabilir.


0

Path.Combine öğesinin ikinci parametresindeki (yol2) başlangıç ​​eğik çizgisini ('\') kaldırın.


Soru bunu sormuyor.
LarsTech

0

Aşağıdaki gibi yolları birleştirmeye zorlamak için toplama işlevi kullandım:

public class MyPath    
{
    public static string ForceCombine(params string[] paths)
    {
        return paths.Aggregate((x, y) => Path.Combine(x, y.TrimStart('\\')));
    }
}

0

Ryan'ın belirttiği gibi, belgelerin tam olarak ne söylediğini yapıyor.

DOS zamanlarından geçerli disk ve geçerli yol ayırt edilir. \kök yolu, ancak MEVCUT DİSK için.

Her " disk " için ayrı bir " geçerli yol " vardır. Kullanarak diski cd D:değiştirirseniz, şu anki yolu değiştirmeyin D:\, ancak şu şekilde değiştirin : "D: \ whatever \ was \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \" \ bu \ disk "...

Yani, pencerelerde bir @"\x"anlam ifade eder: "CURRENTDISK: \ x". Bu nedenle Path.Combine(@"C:\x", @"\y"), ikinci parametre olarak, bilinen bir diskte olmasa da bir akraba değil, bir kök yolu vardır ... Ve hangi "geçerli disk" olabileceği bilinmediğinden, python geri döner "\\y".

>cd C:
>cd \mydironC\apath
>cd D:
>cd \mydironD\bpath
>cd C:
>cd
>C:\mydironC\apath
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.