¿Por qué Path.Combine no concatena correctamente los nombres de archivos que comienzan con Path.DirectorySeparatorChar?
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...
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().
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.
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;
}