절대 경로에서 상대 경로를 얻는 방법


174

내 앱에는 OpenFileDialog를 통해 사용자가로드 한 파일 경로를 표시하는 부분이 있습니다. 전체 경로를 표시하는 데 너무 많은 공간을 차지하고 있지만 모호 할 수 있으므로 파일 이름 만 표시하고 싶지 않습니다. 따라서 assembly / 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 솔루션에 관해서는 나중에 자유 시간에 시도해 볼 것입니다.


답변:


192

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

32
많은 테스트를 거친 후에이 방법이 가장 효과적이었습니다. Uri는 경로 구분 기호로 끝나지 않는 폴더를 파일로 취급합니다 (bar가 폴더 인 경우 c : \ foo \ bar 대신 c : \ foo \ bar \ 사용).
— VVS

6
슬래시 문제에 대한 일반적인 해결책은 다음을 사용하는 것입니다.return relativeUri.ToString().Replace('/',Path.DirectorySeparatorChar);
— Nyerguds

4
유효한 경로를 얻으려면 이렇게 생성 된 상대 URI를 이스케이프 처리해야합니다. .ToString () 표현에는 유효하지 않고 경로에 필요하지 않은 이스케이프 시퀀스가 ​​포함됩니다.
— Eamon Nerbonne 2016 년

3
arg가 if (fromPath.Last ()! = Path.DirectorySeparatorChar) 확인 후 아래를 추가했습니다. {fromPath + = Path.DirectorySeparatorChar; } if (toPath.Last ()! = Path.DirectorySeparatorChar) {toPath + = Path.DirectorySeparatorChar; }
— Simon

8
나를 위해 이것은 상대 경로를 반환 하지 않습니다 . 들어 c:\test하고 c:\test\abc.txt그것을 반환 test\abc.txt내 의견으로는 상대가 아닙니다. 나는 단지 기대할 것이다abc.txt
— juergen d

51

질문에 조금 늦었지만이 기능도 필요했습니다. DavidK에 동의 합니다.이를 제공 하는 내장 API 함수 가 있으므로이를 사용해야합니다. 여기에 관리 래퍼가 있습니다.

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

4
파일이나 경로가 존재하지 않으면 예외가 발생하지 않습니다. 이것이 완전히 합법적 인 사례 일 수 있기 때문입니다.
— VVS

2
그렇다면 GetPathAttributes는 무엇을 반환합니까? "file does not exist"에 대한 플래그가 없으므로 던지기 이외의 실행 가능한 옵션이 표시되지 않습니다. 그렇지 않으면 호출자가 잘못된 정보를 얻습니다.
— ctacke

2
상대 경로를 만들 수 없으면 PathRelativePathTo가 FALSE를 반환합니다. 이 경우 String.Empty를 반환하거나 예외를 throw해야합니다.
— 다니엘 로즈

4
더 명확하게 보입니다 : bool success = PathRelativePathTo (...)와 같은 코드를 사용하면 int의 의미에 대한 설명서를 읽어야하는 int보다 이해하기 쉽습니다.
— Daniel Rose

4
사람 ... 전체 GetPathAttribute를 제거 할 수 있습니다 . 당신이 제공하는 인수가 디렉토리라는 것을 확실히 확신한다면, 0x10을 주면되고, 존재하지 않는 경로로 작동합니다. 그리고 필자의 경우 선호되는 솔루션은 단순히 예외를 던지는 대신 전체 절대 대상 경로를 반환하는 것입니다.
— Nyerguds

31

.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 Interop 답변

상대 경로를 찾는 데 사용할 수있는 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);
}

1
예상했던 것과 훨씬 더 비슷하게 작동합니다. 가장 높은 답을 얻는 대신이 방법을 추천합니다.
— theMayer

당신이 생각 AltDirectorySeparatorChar하는대로 가능성을보고, AppendDirectorySeparatorChar그것을 확인 해서는 안 됩니까?
— 하드 슈나이더

또한 파일에 확장자가 없을 수 있으므로 대부분의 경우 더 편리 할 수 ​​있지만 해당 경우를 지정할 수는 없습니다. 파일 시스템 항목이 존재하는지 확인하고 파일 시스템 폴더인지 확인하십시오. 존재하지 않는 경우이 로직을 유지하십시오. 또는 서명에 파일이나 디렉토리가 제공되었는지 여부를 지정하는 방법을 추가 할 수도 있습니다 (예 : 2 개의 부울).
— 하드 슈나이더

마지막으로 구성표가 다르면 예외가 발생합니다.
— 하드 슈나이더

25

shlwapi.dll에는 Win32 (C ++) 함수가 있으며 원하는 기능을 정확하게 수행합니다. PathRelativePathTo()

P / Invoke 이외의 .NET에서 액세스 할 수있는 방법을 모르겠습니다.


3
어느 부분이 도움이되지 않습니까? PathRelativePathTo ()가 원하는 것을 수행하는 것처럼 원래 게시물을 읽는 것이 좋지만, 아마도 뭔가 잘못 해석 한 것 같습니다.
— DavidK

3
완벽하게 작동합니다. P / Invoke를 설정하는 방법 은 pinvoke.net/default.aspx/shlwapi.PathRelativePathTo 를 참조하십시오 .
— joce April

2
감사합니다! 실제로 C ++ 솔루션을 찾고있었습니다!
— NTDLS

2
기능 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."
— Basic

4
shlwapi.dll 자체가 더 이상 사용되지 않는다고 언급하여 해당 페이지를 읽지 않았습니다. 페이지에 나열된 shlwapi.dll의 래퍼 함수가 더 이상 사용되지 않는다는 것입니다. PathRelativePathTo () 자체는 해당 페이지에서 언급되지 않았으며 PathRelativePathTo ()에 대한 기본 문서는 지원 중단에 대해 언급하지 않았으므로 여전히 유효한 호출 함수입니다.
— DavidK

14

.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 

그렇지 않으면 v4.7부터 .NET 전체 프레임 워크의 경우 다른 제안 된 답변 중 하나를 사용하는 것이 좋습니다.


9

나는 이것을 과거에 사용했습니다.

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

1
나는 그것을 조사 할 것이고, 그것을 테스트하기 위해 언젠가 필요하다. 감사합니다
— 오류

1
Path.GetFullPath ()를 사용하여 두 경로를 상대 비트와 성공적으로 비교하는 것이 좋습니다. 예 : c : \ a \ .. \ b vs. c : \ b vs. c : \ b \. \
— VVS

5

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는이 기능이 가장 먼저 필요하지 않으며이 작업을 수행하는 표준 기능이 있음을 나타내는 답변을 작성했습니다. 그걸 써. 그러나 첫 번째 원칙을 통해 자신의 길을 생각할 수있는 데에는 아무런 해가 없습니다.

유닉스 시스템이 드라이브 문자를 지원하지 않는다는 점을 제외하고 (모든 것이 항상 동일한 루트 디렉토리 아래에 있으므로 첫 번째 글 머리 기호는 관련이 없음) 유닉스에서도 동일한 기술을 사용할 수 있습니다.


4

그것은 먼 길이지만 System.Uri 클래스에는 MakeRelativeUri라는 메서드가 있습니다. 아마도 당신은 그것을 사용할 수 있습니다. System.IO.Path 에이 기능이 없다는 것은 정말 부끄러운 일입니다.


4

위에서 지적 했듯이 .NET Core 2.x에는 Path.GetRelativePath.

아래 코드는 소스에서 수정되었으며 .NET 4.7.1 프레임 워크에서 잘 작동합니다.

// 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과 완벽하게 작동합니다. 이 답변은 공표되거나 해결책으로 받아 들여 져야합니다.
— j00hi 2019

감사합니다! 약간만 수정하면 .NET Framework 4.5.2
— Bja

3

나는 이것을 사용하고있다 :

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

3

사용하다:

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

좁은 케이스 세트의 경우 훌륭하게 작동합니다! 나는 이것을 사용할 것이다! 다른 사람들은 범용 오류 검사, 대소 문자 처리 솔루션을 원한다고 생각합니다. 그러나 이것이 필요한 모든 것이라면 간단합니다!
— DanO

나는을 사용하여 끝났다 path.Replace(rootPath.TrimEnd('\\') + "\\", "").
— Konard

2

절대 경로 2가 항상 절대 경로에 상대적인 것이 확실한 경우 path2에서 첫 번째 N자를 제거하십시오. 여기서 N은 path1의 길이입니다.


2

CommonPath이 RelativePath클래스 의 메소드 를 사용하려고합니다 . 공통 경로가 있으면 표시하려는 경로에서 제거하십시오.

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

1

디렉토리 레벨에서 두 경로를 나눕니다. 여기에서 분기점을 찾아 디렉토리를 전달할 때마다 '../'를 앞에두고 어셈블리 폴더로 돌아가십시오.

그러나 절대 경로는 모든 곳에서 작동하며 일반적으로 상대 경로보다 읽기 쉽습니다. 절대적으로 필요한 경우가 아니라면 개인적으로 사용자에게 상대 경로를 표시하지 않습니다.


완전히 동의합니다. 상대 경로가 전체 경로 이름이 될 수있는 많은 인스턴스가 있습니다. 예를 들어 공통 루트는 드라이브-c : \-이므로이 경우를 처리해야합니다.
— stephbu

1

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

2
다른 폴더에 있으면 어떻게됩니까? 이것은 ".."를 추가하지 않습니다. 경로 중 하나에 ".."이 이미 포함되어 있으면 어떻게합니까? 잘못된 경로의 상대 경로를 반환합니다. 파일 A가 "MyFolder"에 있고 파일 B가 "MyLunchbox"에있는 경우이 방법은 디렉토리 구분 문자에 대해 알지 못하므로 "Lunchbox \ File"이 올바른 경로라고 생각합니다. 이것은 끔찍하다.
— BrainSlugs83

1

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

1

읽기 전용 텍스트 상자가 있으면 레이블로 만들 수없고 AutoEllipsis = true로 설정할 수 없습니까?

또는 자동 줄임표를 직접 생성하기위한 코드가있는 게시물이 있습니다. 계산이 잘못된 위치를 찾지 못했습니다. 원하는 경우 마지막 부분이 아닌 디렉토리의 첫 부분을 제거하도록 쉽게 수정할 수 있습니다.

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

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

0

이것은 작동해야합니다 :

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

0

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

추신 :이 방법의 결과가 내 응용 프로그램에서 uri로 사용되었으므로 Path.DirectorySeparatorChar 대신 구분 기호의 기본값으로 "/"를 사용합니다.


0

여기 내 것이 있습니다 :

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

0

다음과 같이 연주하십시오.

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

0

에서가 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;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.