C# 获取相对于当前工作目录的路径?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/703281/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getting path relative to the current working directory?
提问by Amy
I'm writing a console utility to do some processing on files specified on the commandline, but I've run into a problem I can't solve through Google/Stack Overflow. If a full path, including drive letter, is specified, how do I reformat that path to be relative to the current working directory?
我正在编写一个控制台实用程序来对命令行上指定的文件进行一些处理,但是我遇到了一个无法通过 Google/Stack Overflow 解决的问题。如果指定了完整路径(包括驱动器号),如何将该路径重新格式化为相对于当前工作目录?
There must be something similar to the VirtualPathUtility.MakeRelative function, but if there is, it eludes me.
一定有类似于 VirtualPathUtility.MakeRelative 函数的东西,但如果有,我就躲不开。
采纳答案by Marc Gravell
If you don't mind the slashes being switched, you could [ab]use Uri
:
如果您不介意切换斜线,则可以 [ab] 使用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)
);
As a function/method:
作为函数/方法:
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));
}
回答by Jon Skeet
You can use Environment.CurrentDirectory
to get the current directory, and FileSystemInfo.FullPath
to get the full path to any location. So, fully qualify both the current directory and the file in question, and then check whether the full file name starts with the directory name - if it does, just take the appropriate substring based on the directory name's length.
您可以使用Environment.CurrentDirectory
获取当前目录,以及FileSystemInfo.FullPath
获取任何位置的完整路径。因此,完全限定当前目录和相关文件,然后检查完整文件名是否以目录名开头 - 如果是,只需根据目录名的长度获取适当的子字符串。
Here's some sample code:
这是一些示例代码:
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));
}
}
}
I'm not saying it's the most robust thing in the world (symlinks could probably confuse it) but it's probably okay if this is just a tool you'll be using occasionally.
我并不是说它是世界上最强大的东西(符号链接可能会混淆它),但如果这只是您偶尔使用的工具,那可能没问题。
回答by Dmitry Pavlov
There is also a way to do this with some restrictions. This is the code from the article:
还有一种方法可以通过一些限制来做到这一点。这是文章中的代码:
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();
}
When executing this piece of code:
执行这段代码时:
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));
it prints out:
它打印出:
..\SubDir2\SubDirIWant
..\..\SubDir1
回答by Ben
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;
}
This code is probably not bullet-proof but this is what I came up with. It's a little more robust. It takes two paths and returns path B as relative to path A.
这段代码可能不是防弹的,但这是我想出的。它更坚固一些。它需要两条路径,并返回相对于路径 A 的路径 B。
example:
例子:
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"
回答by Ronnie Overby
Thanks to the other answers here and after some experimentation I've created some very useful extension methods:
感谢这里的其他答案,经过一些实验,我创建了一些非常有用的扩展方法:
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);
}
Important points:
要点:
- Use
FileInfo
andDirectoryInfo
as method parameters so there is no ambiguity as to what is being worked with.Uri.MakeRelativeUri
expects directories to end with a trailing slash. DirectoryInfo.FullName
doesn't normalize the trailing slash. It outputs whatever path was used in the constructor. This extension method takes care of that for you.
- 使用
FileInfo
和DirectoryInfo
作为方法参数,因此对于正在使用的内容没有歧义。Uri.MakeRelativeUri
期望目录以斜杠结尾。 DirectoryInfo.FullName
不规范化尾部斜杠。它输出构造函数中使用的任何路径。此扩展方法会为您处理这些问题。