2009-10-27 9 views
18

Esiste un modo affidabile per calcolare l'inverso di Path.Combine()?Calcolo del percorso relativo ad alcune radici - l'inverso di Path.Combine

Path.Combine ("c: \ folder", "subdirectory \ something.txt") potrebbe restituire qualcosa come "c: \ folder \ subdirectory \ something.text". Quello che voglio è l'inverso, una funzione in cui Path.GetRelativeUrl ("c: \ folder", "c: \ folder \ subdirectory \ something.text") restituirebbe qualcosa come "" sottodirectory \ something.txt ".

Una soluzione consiste nel fare confronti di stringhe e tagliare le radici, ma questo non funzionerebbe se lo stesso percorso è espresso in modi diversi (uso di ".." o "~ 1" nell'espressione di percorso)

+0

I percorsi si trovano sulla stessa macchina? Se non normalizzare potrebbe essere difficile. Vedi http://stackoverflow.com/questions/684684/normalize-file-path-with-winapi/684787 –

+0

Il buon punto è uno scenario difficile quando un percorso è un'unità di rete mappata e l'altro un percorso file locale sulla stessa unità . Fortunatamente non è uno scenario con cui dovrei confrontarmi. –

+0

Rick Strahl ha una soluzione che utilizza la classe Uri: http://west-wind.com/weblog/posts/857279.aspx –

risposta

17

Va bene così nel mio caso non ho alcuni dei casi più difficili (fullPath e relativePath che mescolano le posizioni delle mappe di rete, i nomi di file super lunghi). Quello che ho finito è stato creare la classe sotto

public class PathUtil 
{ 
    static public string NormalizeFilepath(string filepath) 
    { 
     string result = System.IO.Path.GetFullPath(filepath).ToLowerInvariant(); 

     result = result.TrimEnd(new [] { '\\' }); 

     return result; 
    } 

    public static string GetRelativePath(string rootPath, string fullPath) 
    { 
     rootPath = NormalizeFilepath(rootPath); 
     fullPath = NormalizeFilepath(fullPath); 

     if (!fullPath.StartsWith(rootPath)) 
      throw new Exception("Could not find rootPath in fullPath when calculating relative path."); 

     return "." + fullPath.Substring(rootPath.Length); 
    } 
} 

Sembra funzionare piuttosto bene. Almeno, passa questi test NUnit:

[TestFixture] 
public class PathUtilTest 
{ 
    [Test] 
    public void TestDifferencesInCapitolizationDontMatter() 
    { 
     string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); 
     string format2 = PathUtil.NormalizeFilepath("c:\\WindowS\\System32"); 

     Assert.AreEqual(format1, format2); 
    } 

    [Test] 
    public void TestDifferencesDueToBackstepsDontMatter() 
    { 
     string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); 
     string format2 = PathUtil.NormalizeFilepath("c:\\Program Files\\..\\Windows\\System32"); 

     Assert.AreEqual(format1, format2); 
    } 

    [Test] 
    public void TestDifferencesInFinalSlashDontMatter() 
    { 
     string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); 
     string format2 = PathUtil.NormalizeFilepath("c:\\windows\\system32\\"); 

     Console.WriteLine(format1); 
     Console.WriteLine(format2); 

     Assert.AreEqual(format1, format2); 
    } 

    [Test] 
    public void TestCanCalculateRelativePath() 
    { 
     string rootPath = "c:\\windows"; 
     string fullPath = "c:\\windows\\system32\\wininet.dll"; 
     string expectedResult = ".\\system32\\wininet.dll"; 

     string result = PathUtil.GetRelativePath(rootPath, fullPath); 

     Assert.AreEqual(expectedResult, result); 
    } 

    [Test] 
    public void TestThrowsExceptionIfRootDoesntMatchFullPath() 
    { 
     string rootPath = "c:\\windows"; 
     string fullPath = "c:\\program files\\Internet Explorer\\iexplore.exe"; 

     try 
     { 
      PathUtil.GetRelativePath(rootPath, fullPath); 
     } 
     catch (Exception) 
     { 
      return; 
     } 

     Assert.Fail("Exception expected"); 
    } 
} 

I casi di test si basano su determinati file esistenti .. questi file sono comuni sulla maggior parte Windows installa ma la vostra situazione potrebbe essere diversa.

0

Prova percorso .GetFullPath prima e quindi il confronto delle stringhe

5

Ho provato a trovare un modo per farlo con lunghi percorsi di file ma non sto ottenendo risultati soddisfacenti perché si perde la canonicalizzazione dei percorsi in Win32 quando si utilizzano le versioni di percorso lungo di le chiamate standard del file system. Quindi questo la soluzione non funziona necessariamente con cose più lunghe di 260 caratteri, ma è gestito codice e cervello morto semplice altrimenti.

string path1 = @"c:\folder\subdirectory\something.text"; 
string path2 = @"c:\folder\foo\..\something.text"; 
Uri value = new Uri(path1); 
Uri value2 = new Uri(path2); 
Uri result = value.MakeRelativeUri(value2); 
Console.WriteLine(result.OriginalString); 

che dà

../something.text 

Ora i 8.3 nomi (nomi brevi) per i percorsi è una questione diversa. È a mia conoscenza che quei percorsi sono memorizzati nel file system e devi usare win32 per ottenerli. Inoltre possono essere disabilitati quindi non c'è alcuna garanzia che siano lì. Per ottenere il percorso lungo da un percorso breve, chiamare GetLongPathName nel Kernel32.dll. Questo significa anche che il file deve esistere.

Se vuoi farlo, questo sito è tuo amico. GetLongPathName

+0

Oh baby, chi non ama i problemi di percorso? Penso che puntare alla classe Uri sia la migliore risposta comunque, grazie. – anhoppe

3

L'ho fatto con la seguente funzione. Il primo parametro è la directory da dove stavamo cercando, il secondo parametro è il percorso di destinazione. Entrambi i percorsi possono essere relativi. La funzione non è ottimizzata ma fa il suo lavoro.

private string _GetRelativePath(string fromPath, string toPath) 
{ 
    string fromFull = Path.Combine(Environment.CurrentDirectory, fromPath); 
    string toFull = Path.Combine(Environment.CurrentDirectory, toPath); 

    List<string> fromParts = new List<string> 
     fromFull.Split(Path.DirectorySeparatorChar)); 
    List<string> toParts = 
     new List<string>(toFull.Split(Path.DirectorySeparatorChar)); 

    fromParts.RemoveAll(string.IsNullOrEmpty); 
    toParts.RemoveAll(string.IsNullOrEmpty); 

    // Remove all the same parts in front 
    bool areRelative = false; 
    while (fromParts.Count > 0 && toParts.Count > 0 && 
     StringComparer.OrdinalIgnoreCase.Compare(fromParts[0], toParts[0]) == 0) 
    { 
     fromParts.RemoveAt(0); 
     toParts.RemoveAt(0); 

     areRelative = true; 
    } 

    if (!areRelative) 
     return toPath; 

    // Number of remaining fromParts is number of parent dirs 
    StringBuilder ret = new StringBuilder(); 

    for (int i = 0; i < fromParts.Count; i++) 
    { 
     if (ret.Length > 0) 
       ret.Append(Path.DirectorySeparatorChar); 

     ret.Append(".."); 
    } 

    // And the remainder of toParts 
    foreach (string part in toParts) 
    { 
     if (ret.Length > 0) 
       ret.Append(Path.DirectorySeparatorChar); 

     ret.Append(part); 
    } 

    return ret.ToString(); 
}