Вычисление пути относительно некоторого корня-обратный путь.Объединять


Существует ли надежный способ вычисления обратного пути.Объединить ()?

Путь.Комбайн("c:folder", " подкаталогчто-то.txt") может вернуть что-то вроде "c:foldersubdirectorysomething.текст". То, что я хочу, - это обратная функция, где путь.GetRelativeUrl("c:folder", "c:foldersubdirectorysomething.text") вернет что-то вроде ""подкаталогчто-то.формат txt".

Одним из решений является сравнение строк и обрезка корней, но это не будет работать, когда один и тот же путь выражается по-разному (использование ".."или" ~1 " в выражении пути).

4 19

4 ответа:

Хорошо, поэтому в моем случае у меня нет некоторых более жестких крайних случаев (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, но ваш пробег может отличаться.

Я пытался найти способ сделать это с длинными путями к файлам, но я просто не получаю удовлетворительных результатов, потому что вы теряете канонизацию путей в 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, чтобы получить их. Кроме того, они могут быть отключены, так что нет никакой гарантии, что они есть. Чтобы получить длинный путь из короткого пути, вызывается GetLongPathName в ядре 32.файл DLL. Это также означает, что файл должен существовать.

Если вы хотите сделать это, то этот сайт-Ваш друг. GetLongPathName

Я сделал это со следующей функцией. Первый параметр-это каталог, из которого мы ищем, второй параметр-это путь назначения. Оба пути могут быть относительными. Функция не оптимизирована, но выполняет свою работу.

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

Попробуйте Путь.Сначала GetFullPath, а затем сравнение строк.