2008-09-09 4 views
140

Vom Direkt-Fenster in Visual Studio:Warum verkettet Path.Combine nicht korrekt Dateinamen, die mit Path.DirectorySeparatorChar beginnen?

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

Es scheint, dass sie beide gleich sein sollte.

Die alte FileSystemObject.BuildPath() hat auf diese Weise nicht funktionieren ...

+40

OMG das ist so dumm, dass es auf diese Weise "funktioniert". – Joe

+0

[Es ändert sich immer noch nicht im .NET-Kern.] (Https://github.com/dotnet/coreclr/blob/fb86c0294a999b2c7bd1e13da1fdc0d3c2f701e5/src/mscorlib/shared/System/IO/Path.cs#L189) – zwcloud

+0

@Joe Dumm ist richtig! Außerdem muss ich darauf hinweisen, dass [die entsprechende Funktion] (https://nodejs.org/api/path.html#path_path_join_paths) in Node.JS einwandfrei funktioniert ... Kopfschütteln bei Microsoft ... –

Antwort

158

Dies ist eine Art philosophische Frage (die vielleicht nur Microsoft wirklich beantworten kann), da es genau das tut, was die Dokumentation sagt.

System.IO.Path.Combine

"Wenn path2 einen absoluten Pfad enthält, diese Methode path2."

Here's the actual Combine method aus der .NET-Quelle. Sie können sehen, dass es CombineNoChecks aufruft, die dann IsPathRooted auf Pfad2 aufruft und diesen Pfad zurückgibt, wenn dies der Fall ist.

Ich weiß nicht, was die Begründung ist. Ich denke, die Lösung besteht darin, DirectorySeparatorChar vom Anfang des zweiten Pfades abzunehmen (oder zu trimmen); vielleicht schreiben Sie Ihre eigene Combine-Methode, die das tut und dann Path.Combine() aufruft.

+0

Blick auf der zerlegte Code (schaue auf meinen Beitrag), du hast recht. –

+6

Ich würde vermuten, dass es auf diese Weise funktioniert, um einen einfachen Zugriff auf den Algorithmus "current working dir" zu ermöglichen. – BCS

+0

Es scheint zu funktionieren, als würde man eine Sequenz von 'cd (component)' von der Kommandozeile aus tun. Klingt vernünftig für mich. –

5

die tatsächlichen Angaben Nicht zu wissen, ist meine Vermutung, dass es einen Versuch, wie Sie relative URIs beitreten könnten beitreten macht. Zum Beispiel:

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

Dies bedeutet, dass, wenn man einen Weg mit einem vorhergehenden slash verbinden, Sie schließen sie tatsächlich eine Base zu einem anderen, in diesem Fall der zweite Priorität bekommt.

6

Von MSDN:

Wenn einer der angegebenen Pfade ist eine leere Zeichenfolge, Diese Methode liefert den anderen Pfad. Wenn Pfad2 einen absoluten Pfad enthält, gibt diese Methode Pfad2 zurück.

In Ihrem Beispiel ist path2 absolut.

22

Dies ist der demontierte Code von .NET Reflector für Path.Combine-Methode. Überprüfen Sie die IsPathRooted-Funktion. Wenn der zweite Pfad verwurzelt ist (beginnend mit einem DirectorySeparatorChar), geben Sie den zweiten Pfad unverändert zurück.

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

Meiner Meinung nach ist dies ein Fehler. Das Problem ist, dass es zwei verschiedene Arten von "absoluten" Pfaden gibt. Der Pfad "d: \ mydir \ myfile.txt" ist absolut, der Pfad "\ mydir \ myfile.txt" wird ebenfalls als "absolut" betrachtet, obwohl der Laufwerksbuchstabe fehlt. Das richtige Verhalten wäre meiner Meinung nach, den Laufwerksbuchstaben aus dem ersten Pfad voranzustellen, wenn der zweite Pfad mit dem Verzeichnistrennzeichen beginnt (und kein UNC-Pfad ist). Ich würde empfehlen, Ihre eigene Hilfsverpackungsfunktion zu schreiben, die das gewünschte Verhalten hat, wenn Sie es brauchen.

+1

Es entspricht der Spezifikation, aber es ist nicht das, was ich auch erwartet hätte. – dthrasher

+0

@Jake Das vermeidet keinen Bugfix; Das sind mehrere Leute, die lange darüber nachdenken, wie sie etwas tun sollen, und sich dann an das halten, was ihnen zusagt. Beachten Sie auch den Unterschied zwischen dem .Net-Framework (eine Bibliothek, die 'Path.Combine' enthält) und der C# -Sprache. – Grault

0

Dies bedeutet "das Stammverzeichnis des aktuellen Laufwerks". In Ihrem Beispiel bedeutet dies den Ordner "test" im Stammverzeichnis des aktuellen Laufwerks.Also, kann dies mit "c: \ test" gleich

0

Wenn Sie beide Wege kombinieren möchten, ohne jeden Pfad zu verlieren Sie diese verwenden können:

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

Oder mit Variablen:

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

In beiden Fällen wird "C: \ test \ test" zurückgegeben.

Zuerst werte ich aus, ob Path2 mit/beginnt und wenn es wahr ist, gebe Path2 ohne das erste Zeichen zurück. Andernfalls geben Sie den vollständigen Path2 zurück.

+0

Es ist wahrscheinlich sicherer, den '== @" 'Check durch einen' Path.IsRooted() 'Aufruf zu ersetzen, da' "\" 'nicht das einzige zu berücksichtigende Zeichen ist. – rumblefx0

3

Dieser Code sollte es tun:

 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, schon eine lange Liste von Antworten, hier ist mein ;-)

ich dieses Problem lösen wollte:

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

Natürlich sollten alle Pfade 1-9 am Ende eine äquivalente Zeichenfolge enthalten. Hier ist die PathCombine Methode, die ich kam mit:

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

Ich denke auch, dass es sehr ärgerlich ist, dass dieser String-Handling manuell getan werden muss, habe ich im Grund dafür interessieren würde.

0

Dies macht tatsächlich Sinn, in gewisser Weise, wenn man bedenkt, wie (relative) Pfade in der Regel behandelt werden:

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 

Die eigentliche Frage ist, warum Wege, die mit "\" als als „rooted“ beginnen. Das war neu für mich, aber 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 'Rat in seinen "Dingen, die ich über Microsoft Hass" Nach Blog mit dem Titel "Path.Combine is essentially useless.", hier meine Lösung ist:

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

Einige raten dass die Namespaces kollidieren sollten, ... ging ich mit Pathy, wie eine leichte, und Namespace-Kollision mit System.IO.Path zu vermeiden.

bearbeiten: hinzugefügt null Parameter überprüft

0

Diese beiden Methoden sollten Sie versehentlich speichern zwei Strings verbinden, die sowohl das Trennzeichen in ihnen haben.

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