.net - net - Cómo obtener la ruta relativa de la ruta absoluta
ruta relativa c# (21)
Hay una parte en mis aplicaciones que muestra la ruta de archivo cargada por el usuario a través de OpenFileDialog. Se está ocupando demasiado espacio para mostrar toda la ruta, pero no quiero mostrar solo el nombre del archivo, ya que podría ser ambiguo. Por lo tanto, preferiría mostrar la ruta del archivo relativa al directorio assembly / exe.
Por ejemplo, el ensamblado reside en "C: / Archivos de programa / Carpeta Dummy / MyProgram" y el archivo en "C: / Archivos de programa / Carpeta Dummy / MyProgram / Data / datafile1.dat" y luego me gustaría que se muestre ". / Data / datafile1.dat ". Si el archivo está en "C: / Program Files / Dummy Folder / datafile1.dat", entonces quisiera ".. / datafile1.dat". Pero si el archivo está en el directorio raíz o en el directorio 1 debajo de la raíz, entonces muestre la ruta completa.
¿Qué solución recomendarías? Regex?
Básicamente, quiero mostrar información útil sobre la ruta del archivo sin ocupar demasiado espacio en la pantalla.
EDITAR: Solo para aclarar un poco más. El propósito de esta solución es ayudar al usuario o a mí mismo a saber qué archivo cargué por última vez y más o menos desde qué directorio era. Estoy usando un cuadro de texto de solo lectura para mostrar la ruta. La mayoría de las veces, la ruta del archivo es mucho más larga que el espacio de visualización del cuadro de texto. Se supone que la ruta es informativa pero no lo suficientemente importante como para ocupar más espacio en la pantalla.
El comentario de Alex Brault fue bueno, también lo es Jonathan Leffler. La función Win32 proporcionada por DavidK solo ayuda con parte del problema, no con la totalidad, pero gracias de todos modos. En cuanto a la solución de James Newton-King, la probaré más tarde cuando sea libre.
Aquí está el mío:
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()
);
}
Como señala Alex Brault, especialmente en Windows, la ruta absoluta (con letra de unidad y todo) es inequívoca y, a menudo, mejor.
¿No debería su OpenFileDialog usar una estructura de navegador de árbol normal?
Para obtener una nomenclatura en su lugar, el RefDir es el directorio relativo al cual desea especificar la ruta; el AbsName es el nombre de ruta absoluto que desea asignar; y RelPath es la ruta relativa resultante.
Tome la primera de estas opciones que coincida con:
- Si tiene letras de unidad diferentes, no hay una ruta relativa de RefDir a AbsName; debes usar AbsName.
- Si el AbsName está en un subdirectorio de RefDir o es un archivo dentro de RefDir, simplemente elimine el RefDir del comienzo de AbsName para crear RelPath; opcionalmente anteponga "./" (o ". /" ya que está en Windows).
- Encuentre el prefijo común más largo de RefDir y AbsName (donde D: / Abc / Def y D: / Abc / Predeterminado comparte D: / Abc como el prefijo común más largo; tiene que ser un mapeo de los componentes de nombre, no un simple común más largo subcadena); llámalo LCP. Eliminar LCP de AbsName y RefDir. Para cada componente de ruta que quede en (RefDir - LCP), anteponga ".. /" a (AbsName - LCP) para generar RelPath.
Para ilustrar la última regla (que es, por supuesto, de lejos la más compleja), comience con:
RefDir = D:/Abc/Def/Ghi
AbsName = D:/Abc/Default/Karma/Crucible
Entonces
LCP = D:/Abc
(RefDir - LCP) = Def/Ghi
(Absname - LCP) = Default/Karma/Crucible
RelPath = ../../Default/Karma/Crucible
Mientras estaba escribiendo, DavidK produjo una respuesta que sugiere que usted no es el primero en necesitar esta función y que hay una función estándar para hacer este trabajo. Úselo. Pero no hay nada malo en poder pensar a través de los primeros principios, tampoco.
Excepto que los sistemas Unix no admiten letras de unidad (por lo que todo se ubica siempre en el mismo directorio raíz y, por lo tanto, la primera viñeta es irrelevante), la misma técnica podría usarse en Unix.
Desea utilizar el método CommonPath
de esta clase RelativePath
. Una vez que tenga la ruta común, simplemente quítela de la ruta que desea visualizar.
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
Es un camino largo, pero la clase System.Uri tiene un método llamado MakeRelativeUri. Tal vez podrías usar eso. Es una pena realmente que System.IO.Path no tenga esto.
Esto debería funcionar:
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);
}
}
Estoy usando esto:
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();
}
}
Hay una función de Win32 (C ++) en shlwapi.dll que hace exactamente lo que desea: PathRelativePathTo()
No obstante, no estoy al tanto de ninguna forma de acceder a esto desde .NET aparte de P / Invocarlo.
Juega con algo como:
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;
}
Y prueba
[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");
}
}
La forma con Uri no funcionó en sistemas linux / macOS. La ruta ''/ var / www / root'' no se puede convertir a Uri. Manera más universal: hazlo todo con las manos.
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));
}
PD: utilizo "/" como valor predeterminado de separador en lugar de Path.DirectorySeparatorChar, porque el resultado de este método se utiliza como uri en mi aplicación.
La función que usa URI devuelve una ruta "casi" relativa. Incluía un directorio que contiene directamente el archivo cuya ruta relativa quería obtener.
Hace algún tiempo, escribí una función simple que devuelve una ruta relativa de carpeta o archivo, e incluso si está en otra unidad, también incluye la letra de la unidad.
Por favor echa un vistazo:
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;
}
La solución @ Dave no funciona cuando las rutas de archivo no terminan con un ''/'' si la ruta es una ruta de directorio. Esta solución corrige ese problema y también utiliza la constante Uri.UriSchemeFile
lugar de la codificación rígida "FILE"
.
Muchas de las otras soluciones proporcionadas aquí utilizan manipulación de cadenas pero no ofrecen garantías ni indicaciones sobre su fiabilidad, como el número de pruebas unitarias, etc. En general, sugeriría que el uso de Uri.MakeRelativeUri
es la opción más pura de .NET pura, mientras que la mejor alternativa es el ejemplo de interoperabilidad de Windows de @ ctacke.
/// <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;
}
Lo he usado en el pasado.
/// <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;
}
Si está seguro de que su ruta absoluta 2 es siempre relativa a la ruta absoluta, simplemente elimine los primeros N caracteres de la ruta 2, donde N es la longitud de la ruta1.
Si está utilizando .NET Core 2.0, Path.GetRelativePath()
está disponible proporcionando esta funcionalidad específica:
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
De lo contrario, para .NET full framework (a partir de v4.7) recomendamos usar una de las otras respuestas sugeridas.
Si sabes que toPath está contenido en fromPath, puedes mantenerlo simple. Dejaré de lado las afirmaciones por brevedad.
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);
}
Si tiene un cuadro de texto de solo lectura, ¿no podría no convertirlo en una etiqueta y establecer AutoEllipsis = true?
alternativamente, hay publicaciones con código para generar la autoellipsis usted mismo (esto lo hace para una cuadrícula, en su lugar, tendría que pasar el ancho del cuadro de texto. No es del todo correcto, ya que corta un poco más de lo necesario , y no he encontrado el cálculo incorrecto, sería bastante fácil modificar para eliminar la primera parte del directorio en lugar de la última si así lo desea.
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
Un poco tarde para la pregunta, pero solo necesitaba esta característica también. Estoy de acuerdo con DavidK en que, dado que hay una función API incorporada que proporciona esto, debes usarlo. Aquí hay un contenedor administrado:
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);
Utilizar:
RelPath = AbsPath.Replace(ApplicationPath, ".")
Yo dividiría ambas rutas en el nivel del directorio. A partir de ahí, encuentre el punto de divergencia y vuelva a la carpeta de ensamblaje, anteponiendo un ''../'' cada vez que pasa un directorio.
Sin embargo, tenga en cuenta que una ruta absoluta funciona en todas partes y, por lo general, es más fácil de leer que una relativa. Personalmente, no mostraría a un usuario una ruta relativa a menos que fuera absolutamente necesario.
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;
}
/// <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;
}