【问题标题】:How to get relative path from absolute path如何从绝对路径获取相对路径
【发布时间】:2010-09-21 12:00:22
【问题描述】:

我的应用程序中有一部分显示用户通过 OpenFileDialog 加载的文件路径。显示整个路径占用了太多空间,但我不想只显示文件名,因为它可能不明确。所以我更愿意显示相对于程序集/exe 目录的文件路径。

例如,程序集位于C:\Program Files\Dummy Folder\MyProgram,文件位于C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat,那么我希望它显示.\Data\datafile1.dat。如果文件在C:\Program Files\Dummy Folder\datafile1.dat,那么我想要..\datafile1.dat。但如果文件位于根目录或根目录下的 1 个目录,则显示完整路径。

您会推荐什么解决方案?正则表达式?

基本上我想在不占用太多屏幕空间的情况下显示有用的文件路径信息。

编辑:只是为了澄清一点。此解决方案的目的是帮助用户或我自己知道我最后加载了哪个文件以及它来自哪个目录。我正在使用只读文本框来显示路径。大多数时候,文件路径比文本框的显示空间长得多。路径应该提供信息,但不够重要,不会占用更多屏幕空间。

Alex Brault 的评论很好,Jonathan Leffler 也很好。 DavidK 提供的 Win32 功能只能帮助解决部分问题,而不是全部,但无论如何感谢。至于 James Newton-King 的方案,我等有空再试试。

【问题讨论】:

标签: .net


【解决方案1】:

.NET Core 2.0 有 Path.GetRelativePath,否则,使用它。

/// <summary>
/// Creates a relative path from one file or folder to another.
/// </summary>
/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
/// <returns>The relative path from the start directory to the end path or <c>toPath</c> if the paths are not related.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="UriFormatException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public static String MakeRelativePath(String fromPath, String toPath)
{
    if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath");
    if (String.IsNullOrEmpty(toPath))   throw new ArgumentNullException("toPath");

    Uri fromUri = new Uri(fromPath);
    Uri toUri = new Uri(toPath);

    if (fromUri.Scheme != toUri.Scheme) { return toPath; } // path can't be made relative.

    Uri relativeUri = fromUri.MakeRelativeUri(toUri);
    String relativePath = Uri.UnescapeDataString(relativeUri.ToString());

    if (toUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase))
    {
        relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
    }

    return relativePath;
}

【讨论】:

  • 经过大量测试后,这种方法最适合我。您需要记住,Uri 将不以路径分隔符结尾的文件夹视为文件(如果 bar 是文件夹,则使用 c:\foo\bar\ 而不是 c:\foo\bar)。
  • 斜杠问题的一般解决方案是使用return relativeUri.ToString().Replace('/',Path.DirectorySeparatorChar);
  • 您应该取消转义因此创建的相对 uri 以获得有效路径; .ToString() 表示将包含无效且路径中不需要的转义序列。
  • 对我来说,这不是返回相对路径。对于c:\testc:\test\abc.txt,它返回test\abc.txt,在我看来这不是相对的。我只希望abc.txt
  • @juergend:文件夹路径需要以 \ 结尾才能被视为文件夹。在您的情况下,'c:\test\' 和 'c:\test\abc.txt' 将产生正确的结果。不过这有点骇人听闻。
【解决方案2】:

这个问题有点晚了,但我也需要这个功能。我同意 DavidK 的观点,因为有一个 built-in API function 提供了这个,你应该使用它。这是它的托管包装器:

public static string GetRelativePath(string fromPath, string toPath)
{
    int fromAttr = GetPathAttribute(fromPath);
    int toAttr = GetPathAttribute(toPath);

    StringBuilder path = new StringBuilder(260); // MAX_PATH
    if(PathRelativePathTo(
        path,
        fromPath,
        fromAttr,
        toPath,
        toAttr) == 0)
    {
        throw new ArgumentException("Paths must have a common prefix");
    }
    return path.ToString();
}

private static int GetPathAttribute(string path)
{
    DirectoryInfo di = new DirectoryInfo(path);
    if (di.Exists)
    {
        return FILE_ATTRIBUTE_DIRECTORY;
    }

    FileInfo fi = new FileInfo(path);
    if(fi.Exists)
    {
        return FILE_ATTRIBUTE_NORMAL;
    }

    throw new FileNotFoundException();
}

private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
private const int FILE_ATTRIBUTE_NORMAL = 0x80;

[DllImport("shlwapi.dll", SetLastError = true)]
private static extern int PathRelativePathTo(StringBuilder pszPath, 
    string pszFrom, int dwAttrFrom, string pszTo, int dwAttrTo);

【讨论】:

  • 如果文件或路径不存在,我不会抛出异常,因为这可能是完全合法的情况。
  • 那么 GetPathAttributes 会返回什么?没有“文件不存在”的标志,所以除了抛出之外我没有看到任何可行的选项,否则调用者会得到错误的信息。
  • 请注意,如果无法创建相对路径,则 PathRelativePathTo 返回 FALSE。在这种情况下,您应该返回 String.Empty 或抛出异常。
  • 我发现它更清楚:它允许诸如 bool success = PathRelativePathTo(...) 之类的代码,我发现它比 int 更容易理解,您需要阅读有关 int 含义的文档。
  • 人...你可以删除整个GetPathAttribute,你知道的。只要您绝对确定您提供的参数是目录,您只需要给它 0x10 就可以使用完全不存在的路径。在我的情况下,首选的解决方案是简单地返回完整的绝对目标路径,而不是抛出该异常。
【解决方案3】:

.NET Core 2.0 答案

.NET Core 2.0 有Path.GetRelativePath,可以这样使用:

var relativePath = Path.GetRelativePath(
    @"C:\Program Files\Dummy Folder\MyProgram",
    @"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");

在上面的例子中,relativePath 变量等于Data\datafile1.dat

替代 .NET 答案

@Dave 的解决方案在文件路径不以正斜杠字符 (/) 结尾时不起作用,如果路径是目录路径,则可能发生这种情况。我的解决方案解决了这个问题,还使用了Uri.UriSchemeFile 常量而不是硬编码"FILE"

/// <summary>
/// Creates a relative path from one file or folder to another.
/// </summary>
/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>
/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>
/// <returns>The relative path from the start directory to the end path.</returns>
/// <exception cref="ArgumentNullException"><paramref name="fromPath"/> or <paramref name="toPath"/> is <c>null</c>.</exception>
/// <exception cref="UriFormatException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public static string GetRelativePath(string fromPath, string toPath)
{
    if (string.IsNullOrEmpty(fromPath))
    {
        throw new ArgumentNullException("fromPath");
    }

    if (string.IsNullOrEmpty(toPath))
    {
        throw new ArgumentNullException("toPath");
    }

    Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath));
    Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath));

    if (fromUri.Scheme != toUri.Scheme)
    {
        return toPath;
    }

    Uri relativeUri = fromUri.MakeRelativeUri(toUri);
    string relativePath = Uri.UnescapeDataString(relativeUri.ToString());

    if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
    {
        relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
    }

    return relativePath;
}

private static string AppendDirectorySeparatorChar(string path)
{
    // Append a slash only if the path is a directory and does not have a slash.
    if (!Path.HasExtension(path) &&
        !path.EndsWith(Path.DirectorySeparatorChar.ToString()))
    {
        return path + Path.DirectorySeparatorChar;
    }

    return path;
}

Windows 互操作答案

有一个名为PathRelativePathToA 的Windows API 可用于查找相对路径。请注意,您传递给函数的文件或目录路径必须存在才能正常工作。

var relativePath = PathExtended.GetRelativePath(
    @"C:\Program Files\Dummy Folder\MyProgram",
    @"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");

public static class PathExtended
{
    private const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
    private const int FILE_ATTRIBUTE_NORMAL = 0x80;
    private const int MaximumPath = 260;

    public static string GetRelativePath(string fromPath, string toPath)
    {
        var fromAttribute = GetPathAttribute(fromPath);
        var toAttribute = GetPathAttribute(toPath);

        var stringBuilder = new StringBuilder(MaximumPath);
        if (PathRelativePathTo(
            stringBuilder,
            fromPath,
            fromAttribute,
            toPath,
            toAttribute) == 0)
        {
            throw new ArgumentException("Paths must have a common prefix.");
        }

        return stringBuilder.ToString();
    }

    private static int GetPathAttribute(string path)
    {
        var directory = new DirectoryInfo(path);
        if (directory.Exists)
        {
            return FILE_ATTRIBUTE_DIRECTORY;
        }

        var file = new FileInfo(path);
        if (file.Exists)
        {
            return FILE_ATTRIBUTE_NORMAL;
        }

        throw new FileNotFoundException(
            "A file or directory with the specified path was not found.",
            path);
    }

    [DllImport("shlwapi.dll", SetLastError = true)]
    private static extern int PathRelativePathTo(
        StringBuilder pszPath,
        string pszFrom,
        int dwAttrFrom,
        string pszTo,
        int dwAttrTo);
}

【讨论】:

  • 工作方式更像人们期望的那样——我推荐这个而不是最高投票的答案。
  • 看到你认为AltDirectorySeparatorChar 是可能的,AppendDirectorySeparatorChar 不应该也检查一下吗?
  • 另外,一个文件可能没有扩展名,所以虽然这在大多数情况下可能更方便,但它不允许您指定这种情况。也许添加一个检查文件系统条目是否存在,如果存在,检查它是文件还是文件夹。如果它不存在,请保持此逻辑。或者甚至可以在签名中添加某种方式来指定是否提供了文件或目录(例如 2 个布尔值)。
  • 最后,如果方案不同,我会抛出异常。
  • 我发现如果路径不相关,所写的“Alternative .NET Answer”会返回文件 URI 而不是路径(例如“file:///c:/etc”)。为了解决这个问题,我将代码示例中的第 30 行更改为:string relativePath = relativeUri.IsAbsoluteUri ? relativeUri.LocalPath : Uri.UnescapeDataString(relativeUri.ToString());
【解决方案4】:

在 shlwapi.dll 中有一个 Win32 (C++) 函数可以完全满足您的要求:PathRelativePathTo()

不过,除了 P/Invoke 之外,我不知道有任何方法可以从 .NET 访问它。

【讨论】:

  • 对哪一部分没有帮助?阅读原始帖子,在我看来 PathRelativePathTo() 可以满足您的要求,但这可能是因为我误解了某些内容...
  • 完美运行。请参阅pinvoke.net/default.aspx/shlwapi.PathRelativePathTo 了解如何设置 P/Invoke。
  • 谢谢!我实际上是在寻找 C++ 解决方案!
  • 值得注意的是 shlwapi.dll 中的函数现在已被弃用 msdn.microsoft.com/en-us/library/windows/desktop/… "These functions are available through Windows XP Service Pack 2 (SP2) and Windows Server 2003. They might be altered or unavailable in subsequent versions of Windows."
  • 我没有读到该页面说明 shlwapi.dll 本身已被弃用:它所说的只是页面上列出的 shlwapi.dll 的包装函数已弃用。该页面上没有提到 PathRelativePathTo() 本身,PathRelativePathTo() 的主要文档也没有提到弃用,所以据我所知,它仍然是一个有效的调用函数。
【解决方案5】:

如果您使用的是.NET Core 2.0, Path.GetRelativePath(),则可以提供此特定功能:

        var relativeTo = @"C:\Program Files\Dummy Folder\MyProgram";
        var path = @"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat";

        string relativePath = System.IO.Path.GetRelativePath(relativeTo, path);

        System.Console.WriteLine(relativePath);
        // output --> Data\datafile1.dat 

否则,对于 .NET 完整框架(自 v4.7 起),建议使用其他建议的答案之一。

【讨论】:

    【解决方案6】:

    我以前用过这个。

    /// <summary>
    /// Creates a relative path from one file
    /// or folder to another.
    /// </summary>
    /// <param name="fromDirectory">
    /// Contains the directory that defines the
    /// start of the relative path.
    /// </param>
    /// <param name="toPath">
    /// Contains the path that defines the
    /// endpoint of the relative path.
    /// </param>
    /// <returns>
    /// The relative path from the start
    /// directory to the end path.
    /// </returns>
    /// <exception cref="ArgumentNullException"></exception>
    public static string MakeRelative(string fromDirectory, string toPath)
    {
      if (fromDirectory == null)
        throw new ArgumentNullException("fromDirectory");
    
      if (toPath == null)
        throw new ArgumentNullException("toPath");
    
      bool isRooted = (Path.IsPathRooted(fromDirectory) && Path.IsPathRooted(toPath));
    
      if (isRooted)
      {
        bool isDifferentRoot = (string.Compare(Path.GetPathRoot(fromDirectory), Path.GetPathRoot(toPath), true) != 0);
    
        if (isDifferentRoot)
          return toPath;
      }
    
      List<string> relativePath = new List<string>();
      string[] fromDirectories = fromDirectory.Split(Path.DirectorySeparatorChar);
    
      string[] toDirectories = toPath.Split(Path.DirectorySeparatorChar);
    
      int length = Math.Min(fromDirectories.Length, toDirectories.Length);
    
      int lastCommonRoot = -1;
    
      // find common root
      for (int x = 0; x < length; x++)
      {
        if (string.Compare(fromDirectories[x], toDirectories[x], true) != 0)
          break;
    
        lastCommonRoot = x;
      }
    
      if (lastCommonRoot == -1)
        return toPath;
    
      // add relative folders in from path
      for (int x = lastCommonRoot + 1; x < fromDirectories.Length; x++)
      {
        if (fromDirectories[x].Length > 0)
          relativePath.Add("..");
      }
    
      // add to folders to path
      for (int x = lastCommonRoot + 1; x < toDirectories.Length; x++)
      {
        relativePath.Add(toDirectories[x]);
      }
    
      // create relative path
      string[] relativeParts = new string[relativePath.Count];
      relativePath.CopyTo(relativeParts, 0);
    
      string newPath = string.Join(Path.DirectorySeparatorChar.ToString(), relativeParts);
    
      return newPath;
    }
    

    【讨论】:

    • 我会调查一下,我需要一些时间来测试一下。谢谢
    • 我建议使用 Path.GetFullPath() 成功地比较两条路径与相对位。示例:c:\a\..\b 与 c:\b 与 c:\b\.\
    【解决方案7】:

    正如 above 所指出的,.NET Core 2.x 实现了 Path.GetRelativePath

    以下代码改编自源代码,适用于 .NET 4.7.1 Framework。

    // Licensed to the .NET Foundation under one or more agreements.
    // The .NET Foundation licenses this file to you under the MIT license.
    // See the LICENSE file in the project root for more information.
    
    //Adapted from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/Path.cs#L697
    // by Anton Krouglov
    
    using System.Runtime.CompilerServices;
    using System.Diagnostics;
    using System.Text;
    using Xunit;
    
    namespace System.IO {
        // Provides methods for processing file system strings in a cross-platform manner.
        // Most of the methods don't do a complete parsing (such as examining a UNC hostname), 
        // but they will handle most string operations.
        public static class PathNetCore {
    
            /// <summary>
            /// Create a relative path from one path to another. Paths will be resolved before calculating the difference.
            /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix).
            /// </summary>
            /// <param name="relativeTo">The source path the output should be relative to. This path is always considered to be a directory.</param>
            /// <param name="path">The destination path.</param>
            /// <returns>The relative path or <paramref name="path"/> if the paths don't share the same root.</returns>
            /// <exception cref="ArgumentNullException">Thrown if <paramref name="relativeTo"/> or <paramref name="path"/> is <c>null</c> or an empty string.</exception>
            public static string GetRelativePath(string relativeTo, string path) {
                return GetRelativePath(relativeTo, path, StringComparison);
            }
    
            private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) {
                if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo));
                if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
                Debug.Assert(comparisonType == StringComparison.Ordinal ||
                             comparisonType == StringComparison.OrdinalIgnoreCase);
    
                relativeTo = Path.GetFullPath(relativeTo);
                path = Path.GetFullPath(path);
    
                // Need to check if the roots are different- if they are we need to return the "to" path.
                if (!PathInternalNetCore.AreRootsEqual(relativeTo, path, comparisonType))
                    return path;
    
                int commonLength = PathInternalNetCore.GetCommonPathLength(relativeTo, path,
                    ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase);
    
                // If there is nothing in common they can't share the same root, return the "to" path as is.
                if (commonLength == 0)
                    return path;
    
                // Trailing separators aren't significant for comparison
                int relativeToLength = relativeTo.Length;
                if (PathInternalNetCore.EndsInDirectorySeparator(relativeTo))
                    relativeToLength--;
    
                bool pathEndsInSeparator = PathInternalNetCore.EndsInDirectorySeparator(path);
                int pathLength = path.Length;
                if (pathEndsInSeparator)
                    pathLength--;
    
                // If we have effectively the same path, return "."
                if (relativeToLength == pathLength && commonLength >= relativeToLength) return ".";
    
                // We have the same root, we need to calculate the difference now using the
                // common Length and Segment count past the length.
                //
                // Some examples:
                //
                //  C:\Foo C:\Bar L3, S1 -> ..\Bar
                //  C:\Foo C:\Foo\Bar L6, S0 -> Bar
                //  C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar
                //  C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar
    
                StringBuilder
                    sb = new StringBuilder(); //StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length));
    
                // Add parent segments for segments past the common on the "from" path
                if (commonLength < relativeToLength) {
                    sb.Append("..");
    
                    for (int i = commonLength + 1; i < relativeToLength; i++) {
                        if (PathInternalNetCore.IsDirectorySeparator(relativeTo[i])) {
                            sb.Append(DirectorySeparatorChar);
                            sb.Append("..");
                        }
                    }
                }
                else if (PathInternalNetCore.IsDirectorySeparator(path[commonLength])) {
                    // No parent segments and we need to eat the initial separator
                    //  (C:\Foo C:\Foo\Bar case)
                    commonLength++;
                }
    
                // Now add the rest of the "to" path, adding back the trailing separator
                int differenceLength = pathLength - commonLength;
                if (pathEndsInSeparator)
                    differenceLength++;
    
                if (differenceLength > 0) {
                    if (sb.Length > 0) {
                        sb.Append(DirectorySeparatorChar);
                    }
    
                    sb.Append(path, commonLength, differenceLength);
                }
    
                return sb.ToString(); //StringBuilderCache.GetStringAndRelease(sb);
            }
    
            // Public static readonly variant of the separators. The Path implementation itself is using
            // internal const variant of the separators for better performance.
            public static readonly char DirectorySeparatorChar = PathInternalNetCore.DirectorySeparatorChar;
            public static readonly char AltDirectorySeparatorChar = PathInternalNetCore.AltDirectorySeparatorChar;
            public static readonly char VolumeSeparatorChar = PathInternalNetCore.VolumeSeparatorChar;
            public static readonly char PathSeparator = PathInternalNetCore.PathSeparator;
    
            /// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
            internal static StringComparison StringComparison => StringComparison.OrdinalIgnoreCase;
        }
    
        /// <summary>Contains internal path helpers that are shared between many projects.</summary>
        internal static class PathInternalNetCore {
            internal const char DirectorySeparatorChar = '\\';
            internal const char AltDirectorySeparatorChar = '/';
            internal const char VolumeSeparatorChar = ':';
            internal const char PathSeparator = ';';
    
            internal const string ExtendedDevicePathPrefix = @"\\?\";
            internal const string UncPathPrefix = @"\\";
            internal const string UncDevicePrefixToInsert = @"?\UNC\";
            internal const string UncExtendedPathPrefix = @"\\?\UNC\";
            internal const string DevicePathPrefix = @"\\.\";
    
            //internal const int MaxShortPath = 260;
    
            // \\?\, \\.\, \??\
            internal const int DevicePrefixLength = 4;
    
            /// <summary>
            /// Returns true if the two paths have the same root
            /// </summary>
            internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType) {
                int firstRootLength = GetRootLength(first);
                int secondRootLength = GetRootLength(second);
    
                return firstRootLength == secondRootLength
                       && string.Compare(
                           strA: first,
                           indexA: 0,
                           strB: second,
                           indexB: 0,
                           length: firstRootLength,
                           comparisonType: comparisonType) == 0;
            }
    
            /// <summary>
            /// Gets the length of the root of the path (drive, share, etc.).
            /// </summary>
            internal static int GetRootLength(string path) {
                int i = 0;
                int volumeSeparatorLength = 2; // Length to the colon "C:"
                int uncRootLength = 2; // Length to the start of the server name "\\"
    
                bool extendedSyntax = path.StartsWith(ExtendedDevicePathPrefix);
                bool extendedUncSyntax = path.StartsWith(UncExtendedPathPrefix);
                if (extendedSyntax) {
                    // Shift the position we look for the root from to account for the extended prefix
                    if (extendedUncSyntax) {
                        // "\\" -> "\\?\UNC\"
                        uncRootLength = UncExtendedPathPrefix.Length;
                    }
                    else {
                        // "C:" -> "\\?\C:"
                        volumeSeparatorLength += ExtendedDevicePathPrefix.Length;
                    }
                }
    
                if ((!extendedSyntax || extendedUncSyntax) && path.Length > 0 && IsDirectorySeparator(path[0])) {
                    // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo")
    
                    i = 1; //  Drive rooted (\foo) is one character
                    if (extendedUncSyntax || (path.Length > 1 && IsDirectorySeparator(path[1]))) {
                        // UNC (\\?\UNC\ or \\), scan past the next two directory separators at most
                        // (e.g. to \\?\UNC\Server\Share or \\Server\Share\)
                        i = uncRootLength;
                        int n = 2; // Maximum separators to skip
                        while (i < path.Length && (!IsDirectorySeparator(path[i]) || --n > 0)) i++;
                    }
                }
                else if (path.Length >= volumeSeparatorLength &&
                         path[volumeSeparatorLength - 1] == PathNetCore.VolumeSeparatorChar) {
                    // Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:)
                    // If the colon is followed by a directory separator, move past it
                    i = volumeSeparatorLength;
                    if (path.Length >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++;
                }
    
                return i;
            }
    
            /// <summary>
            /// True if the given character is a directory separator.
            /// </summary>
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            internal static bool IsDirectorySeparator(char c) {
                return c == PathNetCore.DirectorySeparatorChar || c == PathNetCore.AltDirectorySeparatorChar;
            }
    
            /// <summary>
            /// Get the common path length from the start of the string.
            /// </summary>
            internal static int GetCommonPathLength(string first, string second, bool ignoreCase) {
                int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase);
    
                // If nothing matches
                if (commonChars == 0)
                    return commonChars;
    
                // Or we're a full string and equal length or match to a separator
                if (commonChars == first.Length
                    && (commonChars == second.Length || IsDirectorySeparator(second[commonChars])))
                    return commonChars;
    
                if (commonChars == second.Length && IsDirectorySeparator(first[commonChars]))
                    return commonChars;
    
                // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar.
                while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1]))
                    commonChars--;
    
                return commonChars;
            }
    
            /// <summary>
            /// Gets the count of common characters from the left optionally ignoring case
            /// </summary>
            internal static unsafe int EqualStartingCharacterCount(string first, string second, bool ignoreCase) {
                if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0;
    
                int commonChars = 0;
    
                fixed (char* f = first)
                fixed (char* s = second) {
                    char* l = f;
                    char* r = s;
                    char* leftEnd = l + first.Length;
                    char* rightEnd = r + second.Length;
    
                    while (l != leftEnd && r != rightEnd
                                        && (*l == *r || (ignoreCase &&
                                                         char.ToUpperInvariant((*l)) == char.ToUpperInvariant((*r))))) {
                        commonChars++;
                        l++;
                        r++;
                    }
                }
    
                return commonChars;
            }
    
            /// <summary>
            /// Returns true if the path ends in a directory separator.
            /// </summary>
            internal static bool EndsInDirectorySeparator(string path)
                => path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]);
        }
    
        /// <summary> Tests for PathNetCore.GetRelativePath </summary>
        public static class GetRelativePathTests {
            [Theory]
            [InlineData(@"C:\", @"C:\", @".")]
            [InlineData(@"C:\a", @"C:\a\", @".")]
            [InlineData(@"C:\A", @"C:\a\", @".")]
            [InlineData(@"C:\a\", @"C:\a", @".")]
            [InlineData(@"C:\", @"C:\b", @"b")]
            [InlineData(@"C:\a", @"C:\b", @"..\b")]
            [InlineData(@"C:\a", @"C:\b\", @"..\b\")]
            [InlineData(@"C:\a\b", @"C:\a", @"..")]
            [InlineData(@"C:\a\b", @"C:\a\", @"..")]
            [InlineData(@"C:\a\b\", @"C:\a", @"..")]
            [InlineData(@"C:\a\b\", @"C:\a\", @"..")]
            [InlineData(@"C:\a\b\c", @"C:\a\b", @"..")]
            [InlineData(@"C:\a\b\c", @"C:\a\b\", @"..")]
            [InlineData(@"C:\a\b\c", @"C:\a", @"..\..")]
            [InlineData(@"C:\a\b\c", @"C:\a\", @"..\..")]
            [InlineData(@"C:\a\b\c\", @"C:\a\b", @"..")]
            [InlineData(@"C:\a\b\c\", @"C:\a\b\", @"..")]
            [InlineData(@"C:\a\b\c\", @"C:\a", @"..\..")]
            [InlineData(@"C:\a\b\c\", @"C:\a\", @"..\..")]
            [InlineData(@"C:\a\", @"C:\b", @"..\b")]
            [InlineData(@"C:\a", @"C:\a\b", @"b")]
            [InlineData(@"C:\a", @"C:\A\b", @"b")]
            [InlineData(@"C:\a", @"C:\b\c", @"..\b\c")]
            [InlineData(@"C:\a\", @"C:\a\b", @"b")]
            [InlineData(@"C:\", @"D:\", @"D:\")]
            [InlineData(@"C:\", @"D:\b", @"D:\b")]
            [InlineData(@"C:\", @"D:\b\", @"D:\b\")]
            [InlineData(@"C:\a", @"D:\b", @"D:\b")]
            [InlineData(@"C:\a\", @"D:\b", @"D:\b")]
            [InlineData(@"C:\ab", @"C:\a", @"..\a")]
            [InlineData(@"C:\a", @"C:\ab", @"..\ab")]
            [InlineData(@"C:\", @"\\LOCALHOST\Share\b", @"\\LOCALHOST\Share\b")]
            [InlineData(@"\\LOCALHOST\Share\a", @"\\LOCALHOST\Share\b", @"..\b")]
            //[PlatformSpecific(TestPlatforms.Windows)]  // Tests Windows-specific paths
            public static void GetRelativePath_Windows(string relativeTo, string path, string expected) {
                string result = PathNetCore.GetRelativePath(relativeTo, path);
                Assert.Equal(expected, result);
    
                // Check that we get the equivalent path when the result is combined with the sources
                Assert.Equal(
                    Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar),
                    Path.GetFullPath(Path.Combine(Path.GetFullPath(relativeTo), result))
                        .TrimEnd(Path.DirectorySeparatorChar),
                    ignoreCase: true,
                    ignoreLineEndingDifferences: false,
                    ignoreWhiteSpaceDifferences: false);
            }
        }
    }
    

    【讨论】:

    • 哇,非常感谢!这与 .NET Framework 4.6.1 完美配合。这个答案应该被赞成甚至接受为解决方案。
    • 谢谢!稍加修改后,它也适用于 .NET Framework 4.5.2
    【解决方案8】:

    正如 Alex Brault 指出的那样,尤其是在 Windows 上,绝对路径(包括驱动器号和所有)是明确的,而且通常更好。

    您的 OpenFileDialog 不应该使用常规的树形浏览器结构吗?

    为了获得一些命名,RefDir 是您要指定路径的相对目录; AbsName 是您要映射的绝对路径名; RelPath 是生成的相对路径。

    选择第一个匹配的选项:

    • 如果您有不同的驱动器号,则没有从 RefDir 到 AbsName 的相对路径;您必须使用 AbsName。
    • 如果 AbsName 位于 RefDir 的子目录中或者是 RefDir 中的文件,则只需从 AbsName 的开头删除 RefDir 即可创建 RelPath;可选地添加“./”(或“.\”,因为您使用的是 Windows)。
    • 查找 RefDir 和 AbsName 的最长公共前缀(其中 D:\Abc\Def 和 D:\Abc\Default 共享 D:\Abc 作为最长公共前缀;它必须是名称组件的映射,而不是简单最长公共子串);称之为 LCP。从 AbsName 和 RefDir 中删除 LCP。对于 (RefDir - LCP) 中留下的每个路径组件,将“..\”添加到 (AbsName - LCP) 以产生 RelPath。

    为了说明最后一条规则(当然,这是迄今为止最复杂的),请从以下开始:

    RefDir = D:\Abc\Def\Ghi
    AbsName = D:\Abc\Default\Karma\Crucible
    

    然后

    LCP = D:\Abc
    (RefDir - LCP) = Def\Ghi
    (Absname - LCP) = Default\Karma\Crucible
    RelPath = ..\..\Default\Karma\Crucible
    

    在我打字时,DavidK 给出了一个答案,表明您不是第一个需要此功能的人,并且有一个标准功能可以完成这项工作。 使用它。但是,能够从第一原则思考你的方式也没有什么坏处。

    除了 Unix 系统不支持驱动器号(所以所有东西总是位于同一个根目录下,因此第一个项目符号无关紧要),同样的技术可以在 Unix 上使用。

    【讨论】:

      【解决方案9】:

      这是一个很长的路要走,但 System.Uri 类有一个名为 MakeRelativeUri 的方法。也许你可以使用它。 System.IO.Path 没有这个真是太可惜了。

      【讨论】:

        【解决方案10】:

        我正在使用这个:

        public static class StringExtensions
        {
          /// <summary>
          /// Creates a relative path from one file or folder to another.
          /// </summary>
          /// <param name="absPath">Absolute path.</param>
          /// <param name="relTo">Directory that defines the start of the relative path.</param> 
          /// <returns>The relative path from the start directory to the end path.</returns>
          public static string MakeRelativePath(this string absPath, string relTo)
          {
              string[] absParts = absPath.Split(Path.DirectorySeparatorChar);
              string[] relParts = relTo.Split(Path.DirectorySeparatorChar);
        
              // Get the shortest of the two paths
              int len = absParts.Length < relParts.Length
                  ? absParts.Length : relParts.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 (absParts[index].Equals(relParts[index], StringComparison.OrdinalIgnoreCase))
                      lastCommonRoot = index;
                  else 
                    break;
              }
        
              // If we didn't find a common prefix then throw
              if (lastCommonRoot == -1)
                  throw new ArgumentException("The path of the two files doesn't have any common base.");
        
              // Build up the relative path
              var relativePath = new StringBuilder();
        
              // Add on the ..
              for (index = lastCommonRoot + 1; index < relParts.Length; index++)
              {
                relativePath.Append("..");
                relativePath.Append(Path.DirectorySeparatorChar);
              }
        
              // Add on the folders
              for (index = lastCommonRoot + 1; index < absParts.Length - 1; index++)
              {
                relativePath.Append(absParts[index]);
                relativePath.Append(Path.DirectorySeparatorChar);
              }
              relativePath.Append(absParts[absParts.Length - 1]);
        
              return relativePath.ToString();
          }
        }
        

        【讨论】:

          【解决方案11】:

          用途:

          RelPath = AbsPath.Replace(ApplicationPath, ".")
          

          【讨论】:

          • 对于一组狭窄的案例,这将非常有效!我要用这个!我认为这些其他人想要一个通用的错误检查、边缘情况处理解决方案。但如果这就是你所需要的,那肯定很简单!
          • 我最终使用了path.Replace(rootPath.TrimEnd('\\') + "\\", "")
          【解决方案12】:

          如果您确定绝对路径 2 始终相对于绝对路径,只需从 path2 中删除前 N 个字符,其中 N 是 path1 的长度。

          【讨论】:

            【解决方案13】:

            您想使用这个RelativePath 类的CommonPath 方法。获得公共路径后,只需将其从要显示的路径中删除即可。

            Namespace IO.Path
            
                Public NotInheritable Class RelativePath
            
                    Private Declare Function PathRelativePathTo Lib "shlwapi" Alias "PathRelativePathToA" ( _
                        ByVal pszPath As String, _
                        ByVal pszFrom As String, _
                        ByVal dwAttrFrom As Integer, _
                        ByVal pszTo As String, _
                        ByVal dwAttrTo As Integer) As Integer
            
                    Private Declare Function PathCanonicalize Lib "shlwapi" Alias "PathCanonicalizeA" ( _
                        ByVal pszBuf As String, _
                        ByVal pszPath As String) As Integer
            
                    Private Const FILE_ATTRIBUTE_DIRECTORY As Short = &H10S
            
                    Private Const MAX_PATH As Short = 260
            
                    Private _path As String
                    Private _isDirectory As Boolean
            
            #Region " Constructors "
            
                    Public Sub New()
            
                    End Sub
            
                    Public Sub New(ByVal path As String)
                        _path = path
                    End Sub
            
                    Public Sub New(ByVal path As String, ByVal isDirectory As Boolean)
                        _path = path
                        _isDirectory = isDirectory
                    End Sub
            
            #End Region
            
                    Private Shared Function StripNulls(ByVal value As String) As String
                        StripNulls = value
                        If (InStr(value, vbNullChar) > 0) Then
                            StripNulls = Left(value, InStr(value, vbNullChar) - 1)
                        End If
                    End Function
            
                    Private Shared Function TrimCurrentDirectory(ByVal path As String) As String
                        TrimCurrentDirectory = path
                        If Len(path) >= 2 And Left(path, 2) = ".\" Then
                            TrimCurrentDirectory = Mid(path, 3)
                        End If
                    End Function
            
                    ''' <summary>
                    ''' 3. conforming to general principles: conforming to accepted principles or standard practice
                    ''' </summary>
                    Public Shared Function Canonicalize(ByVal path As String) As String
                        Dim sPath As String
            
                        sPath = New String(Chr(0), MAX_PATH)
            
                        If PathCanonicalize(sPath, path) = 0 Then
                            Canonicalize = vbNullString
                        Else
                            Canonicalize = StripNulls(sPath)
                        End If
            
                    End Function
            
                    ''' <summary>
                    ''' Returns the most common path between two paths.
                    ''' </summary>
                    ''' <remarks>
                    ''' <para>returns the path that is common between two paths</para>
                    ''' <para>c:\FolderA\FolderB\FolderC</para>
                    '''   c:\FolderA\FolderD\FolderE\File.Ext
                    ''' 
                    '''   results in:
                    '''       c:\FolderA\
                    ''' </remarks>
                    Public Shared Function CommonPath(ByVal path1 As String, ByVal path2 As String) As String
                        'returns the path that is common between two paths
                        '
                        '   c:\FolderA\FolderB\FolderC
                        '   c:\FolderA\FolderD\FolderE\File.Ext
                        '
                        '   results in:
                        '       c:\FolderA\
            
                        Dim sResult As String = String.Empty
                        Dim iPos1, iPos2 As Integer
                        path1 = Canonicalize(path1)
                        path2 = Canonicalize(path2)
                        Do
                            If Left(path1, iPos1) = Left(path2, iPos2) Then
                                sResult = Left(path1, iPos1)
                            End If
                            iPos1 = InStr(iPos1 + 1, path1, "\")
                            iPos2 = InStr(iPos2 + 1, path1, "\")
                        Loop While Left(path1, iPos1) = Left(path2, iPos2)
            
                        Return sResult
            
                    End Function
            
                    Public Function CommonPath(ByVal path As String) As String
                        Return CommonPath(_path, path)
                    End Function
            
                    Public Shared Function RelativePathTo(ByVal source As String, ByVal isSourceDirectory As Boolean, ByVal target As String, ByVal isTargetDirectory As Boolean) As String
                        'DEVLIB
                        '   05/23/05  1:47PM - Fixed call to PathRelativePathTo, iTargetAttribute is now passed to dwAttrTo instead of IsTargetDirectory.
                        '       For Visual Basic 6.0, the fix does not change testing results,
                        '           because when the Boolean IsTargetDirectory is converted to the Long dwAttrTo it happens to contain FILE_ATTRIBUTE_DIRECTORY,
                        '
                        Dim sRelativePath As String
                        Dim iSourceAttribute, iTargetAttribute As Integer
            
                        sRelativePath = New String(Chr(0), MAX_PATH)
                        source = Canonicalize(source)
                        target = Canonicalize(target)
            
                        If isSourceDirectory Then
                            iSourceAttribute = FILE_ATTRIBUTE_DIRECTORY
                        End If
            
                        If isTargetDirectory Then
                            iTargetAttribute = FILE_ATTRIBUTE_DIRECTORY
                        End If
            
                        If PathRelativePathTo(sRelativePath, source, iSourceAttribute, target, iTargetAttribute) = 0 Then
                            RelativePathTo = vbNullString
                        Else
                            RelativePathTo = TrimCurrentDirectory(StripNulls(sRelativePath))
                        End If
            
                    End Function
            
                    Public Function RelativePath(ByVal target As String) As String
                        Return RelativePathTo(_path, _isDirectory, target, False)
                    End Function
            
                End Class
            
            End Namespace
            

            【讨论】:

              【解决方案14】:

              我会在目录级别拆分您的两条路径。从那里,找到分歧点并返回程序集文件夹,每次传递目录时都添加一个“../”。

              但请记住,绝对路径适用于任何地方,并且通常比相对路径更易于阅读。除非绝对必要,否则我个人不会向用户显示相对路径。

              【讨论】:

              • 完全同意 - 在很多情况下,相对路径可能是完整路径名,例如你的共同根目录是驱动器 - c:\ - 所以你仍然需要处理这种情况。
              【解决方案15】:

              如果您知道 toPath 包含在 fromPath 中,那么您可以保持简单。为简洁起见,我将省略断言。

              public static string MakeRelativePath(string fromPath, string toPath)
              {
                  // use Path.GetFullPath to canonicalise the paths (deal with multiple directory seperators, etc)
                  return Path.GetFullPath(toPath).Substring(Path.GetFullPath(fromPath).Length + 1);
              }
              

              【讨论】:

              • 如果它们位于不同的文件夹中怎么办?这不附加“..”。如果其中一条路径已经包含“..”怎么办?这将返回错误级别的相对路径。如果文件 A 在“MyFolder”中而文件 B 在“MyLunchbox”中怎么办——这个方法不知道目录分隔符,所以它只会认为“Lunchbox\File”是正确的路径。这太可怕了。
              【解决方案16】:

              使用 URI 的函数返回“几乎”相对路径。它包含的目录直接包含我想要获取的相对路径的文件。

              前段时间我写了一个简单的函数,它返回文件夹或文件的相对路径,即使它在另一个驱动器上,它也包括驱动器号。

              请看一下:

                  public static string GetRelativePath(string BasePath, string AbsolutePath)
                  {
                      char Separator = Path.DirectorySeparatorChar;
                      if (string.IsNullOrWhiteSpace(BasePath)) BasePath = Directory.GetCurrentDirectory();
                      var ReturnPath = "";
                      var CommonPart = "";
                      var BasePathFolders = BasePath.Split(Separator);
                      var AbsolutePathFolders = AbsolutePath.Split(Separator);
                      var i = 0;
                      while (i < BasePathFolders.Length & i < AbsolutePathFolders.Length)
                      {
                          if (BasePathFolders[i].ToLower() == AbsolutePathFolders[i].ToLower())
                          {
                              CommonPart += BasePathFolders[i] + Separator;
                          }
                          else
                          {
                              break;
                          }
                          i += 1;
                      }
                      if (CommonPart.Length > 0)
                      {
                          var parents = BasePath.Substring(CommonPart.Length - 1).Split(Separator);
                          foreach (var ParentDir in parents)
                          {
                              if (!string.IsNullOrEmpty(ParentDir))
                                  ReturnPath += ".." + Separator;
                          }
                      }
                      ReturnPath += AbsolutePath.Substring(CommonPart.Length);
                      return ReturnPath;
                  }
              

              【讨论】:

                【解决方案17】:

                如果你有一个只读文本框,你能不能不把它做成一个标签并设置 AutoEllipsis=true?

                或者,有一些帖子包含用于自己生成自动省略号的代码:(这适用于网格,您需要将文本框的宽度传递给 i。它不太正确,因为它会破解更多比必要的多,而且我还没有找到计算不正确的地方。 如果您愿意,可以很容易地修改以删除目录的第一部分而不是最后一部分。

                Private Function AddEllipsisPath(ByVal text As String, ByVal colIndex As Integer, ByVal grid As DataGridView) As String
                    'Get the size with the column's width 
                    Dim colWidth As Integer = grid.Columns(colIndex).Width
                
                    'Calculate the dimensions of the text with the current font
                    Dim textSize As SizeF = MeasureString(text, grid.Font)
                
                    Dim rawText As String = text
                    Dim FileNameLen As Integer = text.Length - text.LastIndexOf("\")
                    Dim ReplaceWith As String = "\..."
                
                    Do While textSize.Width > colWidth
                        ' Trim to make room for the ellipsis
                        Dim LastFolder As Integer = rawText.LastIndexOf("\", rawText.Length - FileNameLen - 1)
                
                        If LastFolder < 0 Then
                            Exit Do
                        End If
                
                        rawText = rawText.Substring(0, LastFolder) + ReplaceWith + rawText.Substring(rawText.Length - FileNameLen)
                
                        If ReplaceWith.Length > 0 Then
                            FileNameLen += 4
                            ReplaceWith = ""
                        End If
                        textSize = MeasureString(rawText, grid.Font)
                    Loop
                
                    Return rawText
                End Function
                
                Private Function MeasureString(ByVal text As String, ByVal fontInfo As Font) As SizeF
                    Dim size As SizeF
                    Dim emSize As Single = fontInfo.Size
                    If emSize = 0 Then emSize = 12
                
                    Dim stringFont As New Font(fontInfo.Name, emSize)
                
                    Dim bmp As New Bitmap(1000, 100)
                    Dim g As Graphics = Graphics.FromImage(bmp)
                
                    size = g.MeasureString(text, stringFont)
                    g.Dispose()
                    Return size
                End Function
                

                【讨论】:

                  【解决方案18】:
                      public static string ToRelativePath(string filePath, string refPath)
                      {
                          var pathNormalized = Path.GetFullPath(filePath);
                  
                          var refNormalized = Path.GetFullPath(refPath);
                          refNormalized = refNormalized.TrimEnd('\\', '/');
                  
                          if (!pathNormalized.StartsWith(refNormalized))
                              throw new ArgumentException();
                          var res = pathNormalized.Substring(refNormalized.Length + 1);
                          return res;
                      }
                  

                  【讨论】:

                    【解决方案19】:

                    这应该可行:

                    private string rel(string path) {
                      string[] cwd  = new Regex(@"[\\]").Split(Directory.GetCurrentDirectory());
                      string[] fp   = new Regex(@"[\\]").Split(path);
                    
                      int common = 0;
                    
                      for (int n = 0; n < fp.Length; n++) {
                        if (n < cwd.Length && n < fp.Length && cwd[n] == fp[n]) {
                          common++;
                        }
                      }
                    
                      if (common > 0) {
                        List<string> rp = new List<string>();
                    
                        for (int n = 0; n < (cwd.Length - common); n++) {
                          rp.Add("..");
                        }
                    
                        for (int n = common; n < fp.Length; n++) {
                          rp.Add(fp[n]);
                        }
                    
                        return String.Join("/", rp.ToArray());
                      } else {
                        return String.Join("/", fp);
                      }
                    }
                    

                    【讨论】:

                      【解决方案20】:

                      Uri 的方式不适用于 linux/macOS 系统。路径“/var/www/root”无法转换为 Uri。更通用的方式 - 全部手动完成。

                      public static string MakeRelativePath(string fromPath, string toPath, string sep = "/")
                      {
                          var fromParts = fromPath.Split(new[] { '/', '\\'},
                              StringSplitOptions.RemoveEmptyEntries);
                          var toParts = toPath.Split(new[] { '/', '\\'},
                              StringSplitOptions.RemoveEmptyEntries);
                      
                          var matchedParts = fromParts
                              .Zip(toParts, (x, y) => string.Compare(x, y, true) == 0)
                              .TakeWhile(x => x).Count();
                      
                          return string.Join("", Enumerable.Range(0, fromParts.Length - matchedParts)
                              .Select(x => ".." + sep)) +
                                  string.Join(sep, toParts.Skip(matchedParts));
                      }        
                      

                      PS:我使用“/”作为分隔符的默认值,而不是 Path.DirectorySeparatorChar,因为此方法的结果在我的应用程序中用作 uri。

                      【讨论】:

                        【解决方案21】:

                        这是我的:

                        public static string RelativePathTo(this System.IO.DirectoryInfo @this, string to)
                        {
                            var rgFrom = @this.FullName.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
                            var rgTo = to.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
                            var cSame = rgFrom.TakeWhile((p, i) => i < rgTo.Length && string.Equals(p, rgTo[i])).Count();
                        
                            return Path.Combine(
                                Enumerable.Range(0, rgFrom.Length - cSame)
                                .Select(_ => "..")
                                .Concat(rgTo.Skip(cSame))
                                .ToArray()
                            );
                        }
                        

                        【讨论】:

                          【解决方案22】:

                          玩类似的东西:

                          private String GetRelativePath(Int32 level, String directory, out String errorMessage) {
                                  if (level < 0 || level > 5) {
                                      errorMessage = "Find some more smart input data";
                                      return String.Empty;
                                  }
                                  // ==========================
                                  while (level != 0) {
                                      directory = Path.GetDirectoryName(directory);
                                      level -= 1;
                                  }
                                  // ==========================
                                  errorMessage = String.Empty;
                                  return directory;
                              }
                          

                          测试一下

                          [Test]
                              public void RelativeDirectoryPathTest() {
                                  var relativePath =
                                      GetRelativePath(3, AppDomain.CurrentDomain.BaseDirectory, out var errorMessage);
                                  Console.WriteLine(relativePath);
                                  if (String.IsNullOrEmpty(errorMessage) == false) {
                                      Console.WriteLine(errorMessage);
                                      Assert.Fail("Can not find relative path");
                                  }
                              }
                          

                          【讨论】:

                            【解决方案23】:

                            ASP.NET Core 2 中,如果您想要bin\Debug\netcoreapp2.2 的相对路径,您可以使用以下组合:

                            using Microsoft.AspNetCore.Hosting;
                            using Microsoft.Extensions.Configuration;
                            public class RenderingService : IRenderingService
                            {
                            
                                private readonly IHostingEnvironment _hostingEnvironment;
                                public RenderingService(IHostingEnvironment hostingEnvironment)
                                {
                                _hostingEnvironment = hostingEnvironment;
                                }
                            
                                public string RelativeAssemblyDirectory()
                                {
                                    var contentRootPath = _hostingEnvironment.ContentRootPath;
                                    string executingAssemblyDirectoryAbsolutePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                                    string executingAssemblyDirectoryRelativePath = System.IO.Path.GetRelativePath(contentRootPath, executingAssemblyDirectoryAbsolutePath);
                                    return executingAssemblyDirectoryRelativePath;
                                }
                            }
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2022-06-10
                              • 2011-08-11
                              • 2023-03-22
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多