2009-10-27 101 views
18

的逆是否有計算)Path.Combine的倒數(一種可靠的方法?計算路徑的相對一些根 - Path.Combine

Path.Combine( 「C:\文件夾」, 「子目錄\ something.txt」)可能會返回類似 「C:\文件夾\子目錄\ something.text」。我想要的是反函數,其中Path.GetRelativeUrl(「c:\ folder」,「c:\ folder \ subdirectory \ something.text」)會返回類似於「」子目錄\ something.txt「的函數。

一種解決方案是進行字符串比較並修剪根,但是當以不同的方式表示相同的路徑(在路徑表達式中使用「..」或「〜1」)時,這不起作用。

+0

在同一臺機器上的路徑?如果不正常化可能會很棘手。見http://stackoverflow.com/questions/684684/normalize-file-path-with-winapi/684787 – 2009-10-27 19:36:32

+0

好一點那是一個困難的情況下,當一個路徑是網絡映射驅動器和其他本地文件路徑相同的驅動器上。幸運的是,這不是我不得不面對的情況。 – 2009-10-27 20:50:28

+0

裏克施特拉爾具有使用Uri類的解決方案:http://west-wind.com/weblog/posts/857279.aspx – 2010-12-22 10:30:01

回答

17

好吧,在我的情況下,我沒有一些更強硬的邊緣情況(fullPath和relativePath混合網絡地圖位置,超長文件名)。我最終做的是創建下面的類

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

它似乎工作得很好。至少,它通過了這些測試的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"); 
    } 
} 

測試用例依靠現有的..這些文件是在大多數Windows安裝,但您的里程可能會有所不同常見的某些文件。

0

嘗試路徑.GetFullPath第一,然後字符串比較。

5

我試圖找到一種方式與長文件路徑要做到這一點,但我在使用的長路徑版本,因爲你失去的Win32的路徑規範化我只是沒有得到滿意的結果標準的文件系統調用,所以這個解決方案不一定適用於長度超過260個字符的事物,但它是管理代碼和大腦死亡的簡單方法。

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

其中給出

../something.text 

現在對於路徑的8.3名(短名稱)則是另一回事。我的理解是,這些路徑存儲在文件系統中,並且必須使用win32來獲取它們。另外他們可以被禁用,所以不能保證他們在那裏。要從短路徑中獲得長路徑,請在Kernel32.dll中調用GetLongPathName。這也意味着文件必須存在。

如果你想這樣做,那麼這個網站是你的朋友。 GetLongPathName

+0

寶貝,誰不愛路徑問題?無論如何,我認爲指向Uri班是最好的答案,謝謝。 – anhoppe 2016-09-27 08:42:52

3

我已經完成了以下功能。第一個參數是我們正在查看的目錄,第二個參數是目標路徑。兩條路徑都可以是相對的。該功能沒有優化,但它的工作。

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