J'écris un utilitaire de console pour effectuer certains traitements sur les fichiers spécifiés sur la ligne de commande, mais je rencontre un problème que je ne peux pas résoudre avec Google/Stack Overflow. Si un chemin complet, y compris la lettre du lecteur, est spécifié, comment puis-je reformater ce chemin pour qu'il soit relatif au répertoire de travail en cours?
Il doit y avoir quelque chose de similaire à la fonction VirtualPathUtility.MakeRelative, mais si c'est le cas, cela m'échappe.
Si cela ne vous dérange pas que les barres obliques soient remplacées, vous pouvez [ab] utiliser Uri
:
Uri file = new Uri(@"c:\foo\bar\blop\blap.txt");
// Must end in a slash to indicate folder
Uri folder = new Uri(@"c:\foo\bar\");
string relativePath =
Uri.UnescapeDataString(
folder.MakeRelativeUri(file)
.ToString()
.Replace('/', Path.DirectorySeparatorChar)
);
string GetRelativePath(string filespec, string folder)
{
Uri pathUri = new Uri(filespec);
// Folders must end in a slash
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
folder += Path.DirectorySeparatorChar;
}
Uri folderUri = new Uri(folder);
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
Vous pouvez utiliser Environment.CurrentDirectory
pour obtenir le répertoire actuel et FileSystemInfo.FullPath
pour obtenir le chemin complet vers n'importe quel emplacement. Alors, qualifiez complètement le répertoire en cours et le fichier en question, puis vérifiez si le nom de fichier complet commence par le nom du répertoire. Si c'est le cas, prenez simplement la sous-chaîne appropriée en fonction de la longueur du nom du répertoire.
Voici un exemple de code:
using System;
using System.IO;
class Program
{
public static void Main(string[] args)
{
string currentDir = Environment.CurrentDirectory;
DirectoryInfo directory = new DirectoryInfo(currentDir);
FileInfo file = new FileInfo(args[0]);
string fullDirectory = directory.FullName;
string fullFile = file.FullName;
if (!fullFile.StartsWith(fullDirectory))
{
Console.WriteLine("Unable to make relative path");
}
else
{
// The +1 is to avoid the directory separator
Console.WriteLine("Relative path: {0}",
fullFile.Substring(fullDirectory.Length+1));
}
}
}
Je ne dis pas que c'est la chose la plus robuste au monde (les liens symboliques pourraient probablement le confondre), mais c'est probablement bien si c'est juste un outil que vous utiliserez de temps en temps.
public string MakeRelativePath(string workingDirectory, string fullPath)
{
string result = string.Empty;
int offset;
// this is the easy case. The file is inside of the working directory.
if( fullPath.StartsWith(workingDirectory) )
{
return fullPath.Substring(workingDirectory.Length + 1);
}
// the hard case has to back out of the working directory
string[] baseDirs = workingDirectory.Split(new char[] { ':', '\\', '/' });
string[] fileDirs = fullPath.Split(new char[] { ':', '\\', '/' });
// if we failed to split (empty strings?) or the drive letter does not match
if( baseDirs.Length <= 0 || fileDirs.Length <= 0 || baseDirs[0] != fileDirs[0] )
{
// can't create a relative path between separate harddrives/partitions.
return fullPath;
}
// skip all leading directories that match
for (offset = 1; offset < baseDirs.Length; offset++)
{
if (baseDirs[offset] != fileDirs[offset])
break;
}
// back out of the working directory
for (int i = 0; i < (baseDirs.Length - offset); i++)
{
result += "..\\";
}
// step into the file path
for (int i = offset; i < fileDirs.Length-1; i++)
{
result += fileDirs[i] + "\\";
}
// append the file
result += fileDirs[fileDirs.Length - 1];
return result;
}
Ce code n'est probablement pas à l'épreuve des balles, mais c'est ce que j'ai proposé. C'est un peu plus robuste. Il prend deux chemins et retourne le chemin B relativement au chemin A.
exemple:
MakeRelativePath("c:\\dev\\foo\\bar", "c:\\dev\\junk\\readme.txt")
//returns: "..\\..\\junk\\readme.txt"
MakeRelativePath("c:\\dev\\foo\\bar", "c:\\dev\\foo\\bar\\docs\\readme.txt")
//returns: "docs\\readme.txt"
Grâce aux autres réponses fournies ici et après quelques expériences, j'ai créé des méthodes d'extension très utiles:
public static string GetRelativePathFrom(this FileSystemInfo to, FileSystemInfo from)
{
return from.GetRelativePathTo(to);
}
public static string GetRelativePathTo(this FileSystemInfo from, FileSystemInfo to)
{
Func<FileSystemInfo, string> getPath = fsi =>
{
var d = fsi as DirectoryInfo;
return d == null ? fsi.FullName : d.FullName.TrimEnd('\\') + "\\";
};
var fromPath = getPath(from);
var toPath = getPath(to);
var fromUri = new Uri(fromPath);
var toUri = new Uri(toPath);
var relativeUri = fromUri.MakeRelativeUri(toUri);
var relativePath = Uri.UnescapeDataString(relativeUri.ToString());
return relativePath.Replace('/', Path.DirectorySeparatorChar);
}
Les points importants:
FileInfo
et DirectoryInfo
comme paramètres de méthode pour éviter toute ambiguïté quant à ce qui est travaillé. Uri.MakeRelativeUri
_ s'attend à ce que les répertoires se terminent par une barre oblique.DirectoryInfo.FullName
ne normalise pas la barre oblique finale. Il sort quel que soit le chemin utilisé dans le constructeur. Cette méthode d'extension prend soin de cela pour vous.Il y a aussi ne façon de faire cela avec certaines restrictions . C'est le code de l'article:
public string RelativePath(string absPath, string relTo)
{
string[] absDirs = absPath.Split('\\');
string[] relDirs = relTo.Split('\\');
// Get the shortest of the two paths
int len = absDirs.Length < relDirs.Length ? absDirs.Length : relDirs.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 (absDirs[index] == relDirs[index])
lastCommonRoot = index;
else break;
}
// If we didn't find a common prefix then throw
if (lastCommonRoot == -1)
{
throw new ArgumentException("Paths do not have a common base");
}
// Build up the relative path
StringBuilder relativePath = new StringBuilder();
// Add on the ..
for (index = lastCommonRoot + 1; index < absDirs.Length; index++)
{
if (absDirs[index].Length > 0) relativePath.Append("..\\");
}
// Add on the folders
for (index = lastCommonRoot + 1; index < relDirs.Length - 1; index++)
{
relativePath.Append(relDirs[index] + "\\");
}
relativePath.Append(relDirs[relDirs.Length - 1]);
return relativePath.ToString();
}
Lors de l'exécution de ce morceau de code:
string path1 = @"C:\Inetpub\wwwroot\Project1\Master\Dev\SubDir1";
string path2 = @"C:\Inetpub\wwwroot\Project1\Master\Dev\SubDir2\SubDirIWant";
System.Console.WriteLine (RelativePath(path1, path2));
System.Console.WriteLine (RelativePath(path2, path1));
il imprime:
..\SubDir2\SubDirIWant
..\..\SubDir1