【问题标题】:Getting path relative to the current working directory? [duplicate]获取相对于当前工作目录的路径? [复制]
【发布时间】:2009-03-31 22:09:54
【问题描述】:

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

一定有类似于 VirtualPathUtility.MakeRelative 函数的东西,但如果有的话,我会忽略它。

【问题讨论】:

标签: c# file-io


【解决方案1】:

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

作为一个函数/方法:

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

【讨论】:

  • 不错!只需添加 .Replace('/','\\') 即可让一切变得完美
  • @total: 更好用 .Replace('/', Path.DirectorySeparatorChar)
  • 路径中的空格将变为 %20,您也必须替换它们。也许最好使用 Uri.UnescapeDataString。此外,uri2 的最后一个反斜杠不得省略。
  • 也可以将Environment.CurrentDirectory 用于当前目录。
  • +1 是一个很好的例子。为编辑道歉,但我认为封装的示例可能更受欢迎/可重用。
【解决方案2】:

您可以使用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));
        }
    }
}

我并不是说它是世界上最强大的东西(符号链接可能会混淆它),但如果这只是你偶尔使用的工具,那可能没问题。

【讨论】:

  • 好吧,它不执行当前目录下方的相对路径。我想这会很有趣。
  • 不确定你的意思:c:\Documents and Settings\Jon Skeet\Test> test.exe "c:\Documents and Settings\Jon Skeet\Test\foo\bar" 相对路径:foo \条
  • 换句话说,它对我有用。请给出一个你期望失败的具体例子。
  • 我的意思是,从 C:\TestDir\AnotherDiectory 获取 C:\TestDir\OneDirectory 的相对路径不会返回 ..\OneDirectory。我并不是说它不能改变来做到这一点,它只是不会简单。
  • @LouisRhys:你试过了吗?
【解决方案3】:
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;
}

这段代码可能不是防弹的,但这是我想出的。它更健壮一些。它需要两条路径,并返回相对于路径 A 的路径 B。

示例:

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"

【讨论】:

  • 我知道这已经有一段时间了。但是我发现了一个小错误。在最后一个 for 循环中,您使用 fileDirs[offset] 这应该是 fileDirs[i]
  • 这个答案是我的首选,因为它处理“..”相关目录,这是很快需要的。不确定 Roy 的评论是否仍然有效;它目前显示在我的地方工作。
【解决方案4】:

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

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 不会规范化尾部斜杠。它输出构造函数中使用的任何路径。此扩展方法会为您解决这些问题。

【讨论】:

    【解决方案5】:

    还有a way to do this with some restrictions。这是文章中的代码:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-08
      • 2015-04-07
      • 2012-04-12
      • 2014-03-10
      • 2010-12-28
      • 1970-01-01
      • 2017-02-16
      • 2014-07-29
      相关资源
      最近更新 更多