How to get relative path from absolute path

前端 未结 23 1844
既然无缘
既然无缘 2020-11-22 11:52

There\'s a part in my apps that displays the file path loaded by the user through OpenFileDialog. It\'s taking up too much space to display the whole path, but I don\'t want

相关标签:
23条回答
  • 2020-11-22 12:28

    Way with Uri not worked on linux/macOS systems. Path '/var/www/root' can't be converted to Uri. More universal way - do all by hands.

    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: i use "/" as a default value of separator instead of Path.DirectorySeparatorChar, because result of this method used as uri in my app.

    0 讨论(0)
  • 2020-11-22 12:30

    I'm using this:

    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();
      }
    }
    
    0 讨论(0)
  • 2020-11-22 12:34

    .NET Core 2.0 has Path.GetRelativePath, else, use this.

    /// <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;
    }
    
    0 讨论(0)
  • 2020-11-22 12:36

    This should work:

    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 讨论(0)
  • 2020-11-22 12:38

    You want to use the CommonPath method of this RelativePath class. Once you have the common path, just strip it out of the path you want to display.

    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
    
    0 讨论(0)
  • 2020-11-22 12:39

    If you have a readonly text box, could you not not make it a label and set AutoEllipsis=true?

    alternatively there are posts with code for generating the autoellipsis yourself: (this does it for a grid, you would need to pass i the width for the text box instead. It isn't quite right as it hacks off a bit more than is necessary, and I haven;t got around to finding where the calculation is incorrect. it would be easy enough to modify to remove the first part of the directory rather than the last if you desire.

    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 讨论(0)
提交回复
热议问题