Как получить относительный путь от полного пути

167
задан Andrew Brēza 15 November 2017 в 21:17
поделиться

12 ответов

. СЕТЕВЫЕ Базовые 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;
}
187
ответ дан Ramon Smits 23 November 2019 в 20:59
поделиться

Если у Вас есть текстовое поле только для чтения, разве Вы не могли бы не сделать его маркировкой и установить AutoEllipsis=true?

альтернативно существуют сообщения с кодом для генерации автозамещающего знака самостоятельно: (это делает это для сетки, необходимо было бы передать i ширина для текстового поля вместо этого. Это не совершенно правильно, поскольку это взламывает от немного больше, чем, необходимо, и я приют; t нашел время для нахождения, где вычисление является неправильным. было бы достаточно легко изменить для удаления первой части каталога, а не последнего, если Вы требуете.

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
1
ответ дан Shog9 23 November 2019 в 20:59
поделиться

Я разделил оба из Ваших путей на уровне каталога. Оттуда, найдите точку развилки и работу Вашим путем назад к папке блока, предварительно ожидая a '.. /' каждый раз Вы передаете каталог.

Имеют в виду однако, что полный путь работает везде и обычно легче читать, чем относительный. Я лично не показал бы пользователю относительный путь, если это не было абсолютно необходимо.

1
ответ дан 3Doubloons 23 November 2019 в 20:59
поделиться

Использование:

RelPath = AbsPath.Replace(ApplicationPath, ".")
2
ответ дан Peter Mortensen 23 November 2019 в 20:59
поделиться

Если Вы уверены, что Ваш полный путь 2 всегда относительно полного пути, просто удалите первые символы N из path2, где N является длиной path1.

2
ответ дан 23 November 2019 в 20:59
поделиться

Как Alex Brault указывает, особенно в Windows, полный путь (с буквой диска и всеми) однозначен и часто лучше.

разве Ваш OpenFileDialog не должен использовать регулярную структуру древовидного браузера?

Для получения некоторой номенклатуры на месте RefDir является каталогом, относительно которого Вы хотите определить путь; AbsName является абсолютным путем, который Вы хотите отобразить; и RelPath является получающимся относительным путем.

Выбирают первый из этих вариантов, который соответствует:

  • , Если у Вас есть различные буквы дисков, нет никакого относительного пути от RefDir до AbsName; необходимо использовать AbsName.
  • , Если AbsName находится в подкаталоге RefDir или является файлом в RefDir тогда просто, удаляют RefDir из запуска AbsName для создания RelPath; дополнительно предварительно ожидайте ". /" (или ". \", так как Вы находитесь в Windows).
  • Находят самый длинный общий префикс RefDir и AbsName (где D:\Abc\Def и D:\Abc\Default совместно используют D:\Abc как самый длинный общий префикс; это должно быть отображение компонентов имени, не простая самая длинная общая подстрока); назовите его LCP. Удалите LCP из AbsName и RefDir. Для каждого оставленного внутри компонента контура (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.

5
ответ дан Jonathan Leffler 23 November 2019 в 20:59
поделиться

Это - длинный путь вокруг, но Система. Класс uri имеет метод под названием MakeRelativeUri. Возможно, Вы могли использовать это. Это - позор действительно та Система. IO.Path не имеет этого.

4
ответ дан Vilx- 23 November 2019 в 20:59
поделиться

Я использовал это в прошлом.

/// <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;
}
9
ответ дан James Newton-King 23 November 2019 в 20:59
поделиться

Существует Win32 (C++) функция в shlwapi.dll, который делает точно, что Вы хотите: PathRelativePathTo()

я не знаю ни о каком способе получить доступ к этому от.NET кроме к P/Invoke это, все же.

25
ответ дан Liam 23 November 2019 в 20:59
поделиться

Немного поздно к вопросу, но мне просто была нужна эта функция также. Я соглашаюсь с 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);
50
ответ дан ctacke 23 November 2019 в 20:59
поделиться

В 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;
    }
}
0
ответ дан 23 November 2019 в 20:59
поделиться

Вы хотите использовать метод 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
2
ответ дан 23 November 2019 в 20:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: