Obteniendo una ruta relativa al directorio de trabajo actual?

Estoy escribiendo una utilidad de consola para hacer algún procesamiento en los archivos especificados en la línea de comandos, pero me he encontrado con un problema que no puedo resolver a través de Google / Stack Overflow. Si se especifica una ruta completa, incluida la letra de unidad, ¿cómo puedo reformatear esa ruta para que sea relativa al directorio de trabajo actual?

Debe haber algo similar a la función VirtualPathUtility.MakeRelative, pero si lo hay, me elude.

Si no te molesta que se cambien las barras, podrías [ab] usar Uri :

 Uri file = new Uri(@"c:\foo\bar\blop\blap.txt"); // Must end in a slash to indicate folder Uri folder = new Uri(@"c:\foo\bar\"); string relativePath = Uri.UnescapeDataString( folder.MakeRelativeUri(file) .ToString() .Replace('/', Path.DirectorySeparatorChar) ); 

Como una función / método:

 string GetRelativePath(string filespec, string folder) { Uri pathUri = new Uri(filespec); // Folders must end in a slash if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) { folder += Path.DirectorySeparatorChar; } Uri folderUri = new Uri(folder); return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); } 

Puede usar Environment.CurrentDirectory para obtener el directorio actual y FileSystemInfo.FullPath para obtener la ruta completa a cualquier ubicación. Por lo tanto, califique completamente tanto el directorio actual como el archivo en cuestión, y luego verifique si el nombre completo del archivo comienza con el nombre del directorio; si lo hace, simplemente tome la subcadena correspondiente en función de la longitud del nombre del directorio.

Aquí hay un código de muestra:

 using System; using System.IO; class Program { public static void Main(string[] args) { string currentDir = Environment.CurrentDirectory; DirectoryInfo directory = new DirectoryInfo(currentDir); FileInfo file = new FileInfo(args[0]); string fullDirectory = directory.FullName; string fullFile = file.FullName; if (!fullFile.StartsWith(fullDirectory)) { Console.WriteLine("Unable to make relative path"); } else { // The +1 is to avoid the directory separator Console.WriteLine("Relative path: {0}", fullFile.Substring(fullDirectory.Length+1)); } } } 

No digo que sea la cosa más robusta del mundo (los enlaces simbólicos podrían confundirlo), pero probablemente esté bien si es solo una herramienta que usarás ocasionalmente.

 public string MakeRelativePath(string workingDirectory, string fullPath) { string result = string.Empty; int offset; // this is the easy case. The file is inside of the working directory. if( fullPath.StartsWith(workingDirectory) ) { return fullPath.Substring(workingDirectory.Length + 1); } // the hard case has to back out of the working directory string[] baseDirs = workingDirectory.Split(new char[] { ':', '\\', '/' }); string[] fileDirs = fullPath.Split(new char[] { ':', '\\', '/' }); // if we failed to split (empty strings?) or the drive letter does not match if( baseDirs.Length < = 0 || fileDirs.Length <= 0 || baseDirs[0] != fileDirs[0] ) { // can't create a relative path between separate harddrives/partitions. return fullPath; } // skip all leading directories that match for (offset = 1; offset < baseDirs.Length; offset++) { if (baseDirs[offset] != fileDirs[offset]) break; } // back out of the working directory for (int i = 0; i < (baseDirs.Length - offset); i++) { result += "..\\"; } // step into the file path for (int i = offset; i < fileDirs.Length-1; i++) { result += fileDirs[i] + "\\"; } // append the file result += fileDirs[fileDirs.Length - 1]; return result; } 

Este código probablemente no sea a prueba de balas, pero esto es lo que se me ocurrió. Es un poco más robusto. Toma dos caminos y devuelve la ruta B como relativa a la ruta A.

ejemplo:

 MakeRelativePath("c:\\dev\\foo\\bar", "c:\\dev\\junk\\readme.txt") //returns: "..\\..\\junk\\readme.txt" MakeRelativePath("c:\\dev\\foo\\bar", "c:\\dev\\foo\\bar\\docs\\readme.txt") //returns: "docs\\readme.txt" 

Gracias a las otras respuestas aquí y después de algunos experimentos, he creado algunos métodos de extensión muy útiles:

 public static string GetRelativePathFrom(this FileSystemInfo to, FileSystemInfo from) { return from.GetRelativePathTo(to); } public static string GetRelativePathTo(this FileSystemInfo from, FileSystemInfo to) { Func getPath = fsi => { var d = fsi as DirectoryInfo; return d == null ? fsi.FullName : d.FullName.TrimEnd('\\') + "\\"; }; var fromPath = getPath(from); var toPath = getPath(to); var fromUri = new Uri(fromPath); var toUri = new Uri(toPath); var relativeUri = fromUri.MakeRelativeUri(toUri); var relativePath = Uri.UnescapeDataString(relativeUri.ToString()); return relativePath.Replace('/', Path.DirectorySeparatorChar); } 

Puntos importantes:

  • Use FileInfo y DirectoryInfo como parámetros del método para que no exista ambigüedad con respecto a lo que se está trabajando. Uri.MakeRelativeUri espera que los directorios terminen con una barra inclinada.
  • DirectoryInfo.FullName no normaliza la barra inclinada final. Muestra la ruta que se utilizó en el constructor. Este método de extensión se ocupa de eso por usted.

También hay una forma de hacerlo con algunas restricciones . Este es el código del artículo:

 public string RelativePath(string absPath, string relTo) { string[] absDirs = absPath.Split('\\'); string[] relDirs = relTo.Split('\\'); // Get the shortest of the two paths int len = absDirs.Length < relDirs.Length ? absDirs.Length : relDirs.Length; // Use to determine where in the loop we exited int lastCommonRoot = -1; int index; // Find common root for (index = 0; index < len; index++) { if (absDirs[index] == relDirs[index]) lastCommonRoot = index; else break; } // If we didn't find a common prefix then throw if (lastCommonRoot == -1) { throw new ArgumentException("Paths do not have a common base"); } // Build up the relative path StringBuilder relativePath = new StringBuilder(); // Add on the .. for (index = lastCommonRoot + 1; index < absDirs.Length; index++) { if (absDirs[index].Length > 0) relativePath.Append("..\\"); } // Add on the folders for (index = lastCommonRoot + 1; index < relDirs.Length - 1; index++) { relativePath.Append(relDirs[index] + "\\"); } relativePath.Append(relDirs[relDirs.Length - 1]); return relativePath.ToString(); } 

Al ejecutar esta pieza de código:

 string path1 = @"C:\Inetpub\wwwroot\Project1\Master\Dev\SubDir1"; string path2 = @"C:\Inetpub\wwwroot\Project1\Master\Dev\SubDir2\SubDirIWant"; System.Console.WriteLine (RelativePath(path1, path2)); System.Console.WriteLine (RelativePath(path2, path1)); 

imprime:

 ..\SubDir2\SubDirIWant ..\..\SubDir1