获取相对于当前工作目录的path?

我正在编写一个控制台实用程序来对命令行上指定的文件进行一些处理,但是我遇到了一个我无法通过Google / Stack Overflow解决的问题。 如果指定了完整path(包括驱动器盘符),如何将该path重新格式化为相对于当前工作目录?

必须有类似于VirtualPathUtility.MakeRelative函数的东西,但是如果有的话,它就没有了。

如果你不介意切换的斜杠,你可以使用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) ); 

作为一种function/方法:

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

您可以使用Environment.CurrentDirectory获取当前目录,并使用FileSystemInfo.FullPath获取任何位置的完整path。 因此,完全限定当前目录和有问题的文件,然后检查完​​整的文件名是否以目录名开始 – 如果是,则根据目录名的长度取适当的子string。

以下是一些示例代码:

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

我并不是说这是世界上最强大的事情(符号链接可能会混淆),但如果这只是偶尔使用的工具,那也可能是好的。

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

这个代码可能不是防弹的,但是这是我想出来的。 这是一个更强大一点。 它需要两条path,并返回相对于pathA的pathB.

例:

 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" 

感谢这里的其他答案和一些实验后,我创build了一些非常有用的扩展方法:

 public static string GetRelativePathFrom(this FileSystemInfo to, FileSystemInfo from) { return from.GetRelativePathTo(to); } public static string GetRelativePathTo(this FileSystemInfo from, FileSystemInfo to) { Func<FileSystemInfo, string> 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); } 

重要的一点:

  • 使用FileInfoDirectoryInfo作为方法参数,所以对于正在处理的内容没有歧义。 Uri.MakeRelativeUri预计目录以结尾的斜杠结束。
  • DirectoryInfo.FullName不标准化结尾的斜杠。 它输出在构造函数中使用的任何path。 这种扩展方法为你照顾。

还有一种方法可以做一些限制 。 这是来自文章的代码:

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

执行这段代码时:

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

它打印出来:

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