75 votes

L'obtention de chemin relatif au répertoire de travail actuel?

Je suis en train d'écrire un utilitaire de console pour effectuer un traitement sur les fichiers spécifiés sur la ligne de commande, mais j'ai rencontré un problème que je ne peux pas résoudre par le biais de Google/. Si un chemin d'accès complet, y compris la lettre de lecteur, est spécifié, comment dois-je reformater le chemin relatif au répertoire de travail courant?

Il doit y avoir quelque chose de similaire à la VirtualPathUtility.MakeRelative fonction, mais si il y est, il m'échappe.

129voto

Marc Gravell Points 482669

Si vous n'avez pas l'esprit de la slash en train d'être réglé, vous pouvez [ab] 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)
    );

Comme une fonction/méthode:

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

Toutes mes excuses à Marc pour l'ajout de cette. Je préfère encapsulé exemples, et cela peut sauver d'autres, de frappe (votre exemple était excellent donc pas de point en ajoutant une autre réponse basée sur elle) - hi-tech de la Magie :)

38voto

Jon Skeet Points 692016

Vous pouvez utiliser Environment.CurrentDirectory pour obtenir le répertoire courant, et FileSystemInfo.FullPath pour obtenir le chemin d'accès complet à n'importe quel endroit. Aussi, tout en se qualifier à la fois le répertoire courant et le fichier en question, et ensuite, vérifiez si le nom complet du fichier commence par le nom du répertoire - si il ne, juste de prendre les mesures appropriées de sous-chaîne basé sur le nom du répertoire de la longueur.

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 le plus robuste chose dans le monde (les liens symboliques pourrait probablement confondre), mais c'est probablement acceptable si c'est juste un outil que vous allez utiliser de temps en temps.

9voto

Ben Points 23
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[offset] + "\\";
    }

    // 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 je suis venu avec. C'est un peu plus robuste. Il prend deux chemins et renvoie le chemin B par rapport à la trajectoire 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"

4voto

Ronnie Overby Points 11402

Merci pour les autres réponses ici, et après quelques essais, j'ai créé une certaine extension très utile méthodes:

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

Points importants:

  • Utiliser FileInfo et DirectoryInfo que les paramètres de la méthode, donc il n'y a aucune ambiguïté quant à ce qui est travaillé avec. Uri.MakeRelativeUri attend répertoires pour terminer par une barre oblique.
  • DirectoryInfo.FullName n'est pas de normaliser le slash. C'sorties quelque soit le chemin a été utilisé dans le constructeur. Cette méthode d'extension prend en charge pour vous.

2voto

Dmitry Pavlov Points 2260

Il y a aussi moyen de faire cela avec certaines restrictions: http://www.iandevlin.com/blog/2010/01/csharp/generating-a-relative-path-in-csharp/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X