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

Amy*_*Amy 74 c# file-io

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

必须有类似于VirtualPathUtility.MakeRelative函数的东西,但如果有,它就会让我失望.

Mar*_*ell 129

如果你不介意切换斜线,你可以[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)
    );
Run Code Online (Sandbox Code Playgroud)

作为一种功能/方法:

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));
}
Run Code Online (Sandbox Code Playgroud)

  • 路径中的空格将变为%20,您还必须更换它们.也许最好使用Uri.UnescapeDataString.此外,不得省略uri2的最后反斜杠. (16认同)
  • @total:更好用.Replace('/',Path.DirectorySeparatorChar) (12认同)
  • +1非常好的例子.为编辑道歉,但我认为封装的例子可能更受欢迎/可重用. (4认同)
  • 太好了!只需添加.Replace('/','\\')就可以完美无缺 (2认同)
  • 也可以使用`Environment.CurrentDirectory`作为当前目录. (2认同)

Jon*_*eet 38

您可以使用Environment.CurrentDirectory获取当前目录,并FileSystemInfo.FullPath获取任何位置的完整路径.因此,完全限定当前目录和相关文件,然后检查完​​整文件名是否以目录名开头 - 如果是,只需根据目录名的长度获取相应的子字符串.

这是一些示例代码:

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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不是说它是世界上最强大的东西(符号链接可能会让它混淆)但是如果这只是一个偶尔会使用的工具,那也许没关系.

  • 我的意思是,从C:\ TestDir\AnotherDiectory获取C:\ TestDir\OneDirectory的相对路径不会返回..\OneDirectory.我不是说不能改变这样做,它不会简单. (7认同)
  • 好吧,它不会执行当前目录下的相对路径.我想这会很有趣. (3认同)
  • @LouisRhys:你有没有试过? (2认同)

Ben*_*Ben 8

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;
}
Run Code Online (Sandbox Code Playgroud)

这段代码可能不是防弹,但这就是我提出的.它更健壮一点.它需要两条路径并返回路径B作为相对于路径A的路径.

例:

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"
Run Code Online (Sandbox Code Playgroud)

  • 我知道这已经有一段时间了.但我发现了一个小虫子.在最后一个for循环中,你使用`fileDirs [offset]`这应该是`fileDirs [i]` (3认同)

Ron*_*rby 5

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

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);
}
Run Code Online (Sandbox Code Playgroud)

要点:

  • 使用FileInfoDirectoryInfo作为方法参数,这样就不会不清楚正在处理的内容。Uri.MakeRelativeUri期望目录以斜杠结尾。
  • DirectoryInfo.FullName不规范尾部斜杠。它输出构造函数中使用的任何路径。这个扩展方法会为你解决这个问题。