.net - getfullpath - relative path c#
Cálculo de la ruta relativa a alguna raíz, el inverso de Path.Combine (4)
¿Hay una manera confiable de calcular el inverso de Path.Combine ()?
Path.Combine ("c: / folder", "subdirectory / something.txt") puede devolver algo como "c: / folder / subdirectory / something.text". Lo que quiero es lo inverso, una función donde Path.GetRelativeUrl ("c: / folder", "c: / folder / subdirectory / something.text") devolverá algo como "" subdirectory / something.txt ".
Una solución es hacer comparaciones de cadenas y recortar las raíces, pero esto no funcionaría cuando la misma ruta se exprese de diferentes maneras (uso de ".." o "~ 1" en la expresión de la ruta).
De acuerdo, en mi caso no tengo algunos de los casos más difíciles (ubicaciones de mapas de red de mezcla de FullPath y relativePath, nombres de archivos super largos). Lo que terminé haciendo fue crear la clase a continuación
public class PathUtil
{
static public string NormalizeFilepath(string filepath)
{
string result = System.IO.Path.GetFullPath(filepath).ToLowerInvariant();
result = result.TrimEnd(new [] { ''//' });
return result;
}
public static string GetRelativePath(string rootPath, string fullPath)
{
rootPath = NormalizeFilepath(rootPath);
fullPath = NormalizeFilepath(fullPath);
if (!fullPath.StartsWith(rootPath))
throw new Exception("Could not find rootPath in fullPath when calculating relative path.");
return "." + fullPath.Substring(rootPath.Length);
}
}
Parece funcionar bastante bien. Al menos, pasa estas pruebas NUnit:
[TestFixture]
public class PathUtilTest
{
[Test]
public void TestDifferencesInCapitolizationDontMatter()
{
string format1 = PathUtil.NormalizeFilepath("c://windows//system32");
string format2 = PathUtil.NormalizeFilepath("c://WindowS//System32");
Assert.AreEqual(format1, format2);
}
[Test]
public void TestDifferencesDueToBackstepsDontMatter()
{
string format1 = PathUtil.NormalizeFilepath("c://windows//system32");
string format2 = PathUtil.NormalizeFilepath("c://Program Files//..//Windows//System32");
Assert.AreEqual(format1, format2);
}
[Test]
public void TestDifferencesInFinalSlashDontMatter()
{
string format1 = PathUtil.NormalizeFilepath("c://windows//system32");
string format2 = PathUtil.NormalizeFilepath("c://windows//system32//");
Console.WriteLine(format1);
Console.WriteLine(format2);
Assert.AreEqual(format1, format2);
}
[Test]
public void TestCanCalculateRelativePath()
{
string rootPath = "c://windows";
string fullPath = "c://windows//system32//wininet.dll";
string expectedResult = ".//system32//wininet.dll";
string result = PathUtil.GetRelativePath(rootPath, fullPath);
Assert.AreEqual(expectedResult, result);
}
[Test]
public void TestThrowsExceptionIfRootDoesntMatchFullPath()
{
string rootPath = "c://windows";
string fullPath = "c://program files//Internet Explorer//iexplore.exe";
try
{
PathUtil.GetRelativePath(rootPath, fullPath);
}
catch (Exception)
{
return;
}
Assert.Fail("Exception expected");
}
}
Los casos de prueba dependen de ciertos archivos existentes. Estos archivos son comunes en la mayoría de las instalaciones de Windows, pero su kilometraje puede variar.
Intenté encontrar una forma de hacerlo con rutas de archivo largas, pero no obtengo resultados satisfactorios porque pierde la canonicalización de las rutas en Win32 cuando utiliza las versiones de ruta larga de las llamadas del sistema de archivos estándar. Por lo tanto, esta solución no necesariamente funciona con cosas de más de 260 caracteres, pero de lo contrario, es un código administrado y un cerebro muerto.
string path1 = @"c:/folder/subdirectory/something.text";
string path2 = @"c:/folder/foo/../something.text";
Uri value = new Uri(path1);
Uri value2 = new Uri(path2);
Uri result = value.MakeRelativeUri(value2);
Console.WriteLine(result.OriginalString);
Lo que da
../something.text
Ahora los 8.3 nombres (nombres cortos) para las rutas son un asunto diferente. Tengo entendido que esas rutas se almacenan en el sistema de archivos y usted tiene que usar win32 para obtenerlas. Además, se pueden desactivar, por lo que no hay garantía de que estén allí. Para obtener la ruta larga desde una ruta corta, llame a GetLongPathName en Kernel32.dll. Esto también significa que el archivo tiene que existir.
Si quieres hacer eso, entonces este sitio es tu amigo. GetLongPathName
Lo he hecho con la siguiente función. El primer parámetro es el directorio desde donde estamos buscando, el segundo parámetro es la ruta de destino. Ambos caminos pueden ser relativos. La función no está optimizada pero hace su trabajo.
private string _GetRelativePath(string fromPath, string toPath)
{
string fromFull = Path.Combine(Environment.CurrentDirectory, fromPath);
string toFull = Path.Combine(Environment.CurrentDirectory, toPath);
List<string> fromParts = new List<string>
fromFull.Split(Path.DirectorySeparatorChar));
List<string> toParts =
new List<string>(toFull.Split(Path.DirectorySeparatorChar));
fromParts.RemoveAll(string.IsNullOrEmpty);
toParts.RemoveAll(string.IsNullOrEmpty);
// Remove all the same parts in front
bool areRelative = false;
while ( fromParts.Count > 0 && toParts.Count > 0 &&
StringComparer.OrdinalIgnoreCase.Compare(fromParts[0], toParts[0]) == 0 )
{
fromParts.RemoveAt(0);
toParts.RemoveAt(0);
areRelative = true;
}
if ( !areRelative )
return toPath;
// Number of remaining fromParts is number of parent dirs
StringBuilder ret = new StringBuilder();
for ( int i = 0; i < fromParts.Count; i++ )
{
if ( ret.Length > 0 )
ret.Append(Path.DirectorySeparatorChar);
ret.Append("..");
}
// And the remainder of toParts
foreach (string part in toParts)
{
if ( ret.Length > 0 )
ret.Append(Path.DirectorySeparatorChar);
ret.Append(part);
}
return ret.ToString();
}
Pruebe primero Path.GetFullPath y luego la comparación de cadenas.