¿Por qué Path.Combine no concatena correctamente los nombres de archivos que comienzan con Path.DirectorySeparatorChar?

Resuelto Kris Erickson asked hace 16 años • 16 respuestas

Desde la ventana Inmediato en Visual Studio:

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

Parece que ambos deberían ser iguales.

El antiguo FileSystemObject.BuildPath() no funcionaba de esta manera...

Kris Erickson avatar Sep 10 '08 06:09 Kris Erickson
Aceptado

Esta es una especie de pregunta filosófica (que quizás sólo Microsoft pueda responder realmente), ya que está haciendo exactamente lo que dice la documentación.

System.IO.Path.Combinar

"Si la ruta2 contiene una ruta absoluta, este método devuelve la ruta2".

Aquí está el método Combine real de la fuente .NET. Puedes ver que llama a CombineNoChecks , que luego llama a IsPathRooted en la ruta2 y devuelve esa ruta si es así:

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

No sé cuál es el motivo. Supongo que la solución es quitar (o recortar) DirectorySeparatorChar del principio de la segunda ruta; tal vez escriba su propio método Combine que haga eso y luego llame a Path.Combine().

Ryan Lundy avatar Sep 09 '2008 23:09 Ryan Lundy

Quería resolver este problema:

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

Por supuesto, todas las rutas 1 a 9 deben contener una cadena equivalente al final. Aquí está el método PathCombine que se me ocurrió:

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

También creo que es bastante molesto que este manejo de cadenas deba realizarse manualmente, y me interesaría saber el motivo detrás de esto.

anhoppe avatar Jun 30 '2015 06:06 anhoppe

Este es el código desensamblado del método .NET Reflector para Path.Combine. Verifique la función IsPathRooted. Si la segunda ruta tiene raíz (comienza con DirectorySeparatorChar), devuelva la segunda ruta tal como está.

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;
}
Gulzar Nazim avatar Sep 09 '2008 23:09 Gulzar Nazim