Почему Path.Combine неправильно объединяет имена файлов, начинающиеся с Path.DirectorySeparatorChar? - PullRequest
161 голосов
/ 10 сентября 2008

Из Непосредственного окна в Visual Studio:

> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"

Кажется, они оба должны быть одинаковыми.

Старый FileSystemObject.BuildPath () не работал таким образом ...

Ответы [ 13 ]

185 голосов
/ 10 сентября 2008

Это своего рода философский вопрос (на который, пожалуй, может ответить только Microsoft), поскольку он делает именно то, что написано в документации.

System.IO.Path.Combine

"Если path2 содержит абсолютный путь, этот метод возвращает path2."

Вот фактический метод Combine из источника .NET. Вы можете видеть, что он вызывает CombineNoChecks , который затем вызывает IsPathRooted на path2 и возвращает этот путь, если так:

public static String Combine(String path1, String path2) {
    if (path1==null || path2==null)
        throw new ArgumentNullException((path1==null) ? "path1" : "path2");
    Contract.EndContractBlock();
    CheckInvalidPathChars(path1);
    CheckInvalidPathChars(path2);

    return CombineNoChecks(path1, path2);
}

internal static string CombineNoChecks(string path1, string path2)
{
    if (path2.Length == 0)
        return path1;

    if (path1.Length == 0)
        return path2;

    if (IsPathRooted(path2))
        return path2;

    char ch = path1[path1.Length - 1];
    if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar &&
            ch != VolumeSeparatorChar) 
        return path1 + DirectorySeparatorCharAsString + path2;
    return path1 + path2;
}

Я не знаю, в чем причина. Я думаю, что решение состоит в том, чтобы убрать (или обрезать) DirectorySeparatorChar из начала второго пути; возможно, напишите свой собственный метод Combine, который делает это, а затем вызывает Path.Combine ().

22 голосов
/ 10 сентября 2008

Это дизассемблированный код из .NET Reflector для метода Path.Combine. Проверьте функцию IsPathRooted. Если второй путь является корневым (начинается с DirectorySeparatorChar), верните второй путь как есть.

public static string Combine(string path1, string path2)
{
    if ((path1 == null) || (path2 == null))
    {
        throw new ArgumentNullException((path1 == null) ? "path1" : "path2");
    }
    CheckInvalidPathChars(path1);
    CheckInvalidPathChars(path2);
    if (path2.Length == 0)
    {
        return path1;
    }
    if (path1.Length == 0)
    {
        return path2;
    }
    if (IsPathRooted(path2))
    {
        return path2;
    }
    char ch = path1[path1.Length - 1];
    if (((ch != DirectorySeparatorChar) &&
         (ch != AltDirectorySeparatorChar)) &&
         (ch != VolumeSeparatorChar))
    {
        return (path1 + DirectorySeparatorChar + path2);
    }
    return (path1 + path2);
}


public static bool IsPathRooted(string path)
{
    if (path != null)
    {
        CheckInvalidPathChars(path);
        int length = path.Length;
        if (
              (
                  (length >= 1) &&
                  (
                      (path[0] == DirectorySeparatorChar) ||
                      (path[0] == AltDirectorySeparatorChar)
                  )
              )

              ||

              ((length >= 2) &&
              (path[1] == VolumeSeparatorChar))
           )
        {
            return true;
        }
    }
    return false;
}
20 голосов
/ 30 июня 2015

Я хотел решить эту проблему:

string sample1 = "configuration/config.xml";
string sample2 = "/configuration/config.xml";
string sample3 = "\\configuration/config.xml";

string dir1 = "c:\\temp";
string dir2 = "c:\\temp\\";
string dir3 = "c:\\temp/";

string path1 = PathCombine(dir1, sample1);
string path2 = PathCombine(dir1, sample2);
string path3 = PathCombine(dir1, sample3);

string path4 = PathCombine(dir2, sample1);
string path5 = PathCombine(dir2, sample2);
string path6 = PathCombine(dir2, sample3);

string path7 = PathCombine(dir3, sample1);
string path8 = PathCombine(dir3, sample2);
string path9 = PathCombine(dir3, sample3);

Конечно, все пути 1-9 должны содержать эквивалентную строку в конце. Вот метод PathCombine, который я придумал:

private string PathCombine(string path1, string path2)
{
    if (Path.IsPathRooted(path2))
    {
        path2 = path2.TrimStart(Path.DirectorySeparatorChar);
        path2 = path2.TrimStart(Path.AltDirectorySeparatorChar);
    }

    return Path.Combine(path1, path2);
}

Я также думаю, что довольно неприятно, что эту обработку строк нужно выполнять вручную, и мне было бы интересно узнать причину этого.

16 голосов
/ 10 сентября 2008

На мой взгляд, это ошибка. Проблема в том, что существует два разных типа «абсолютных» путей. Путь "d: \ mydir \ myfile.txt" является абсолютным, путь "\ mydir \ myfile.txt" также считается "абсолютным", даже если в нем отсутствует буква диска. На мой взгляд, правильное поведение заключается в добавлении буквы диска из первого пути, когда второй путь начинается с разделителя каталогов (а не UNC-путь). Я бы порекомендовал написать свою собственную вспомогательную функцию-обертку, которая будет иметь желаемое поведение, если вам это нужно.

7 голосов
/ 10 сентября 2008

С MSDN :

Если один из указанных путей является строкой нулевой длины, этот метод возвращает другой путь. Если path2 содержит абсолютный путь, этот метод возвращает path2.

В вашем примере path2 является абсолютным.

6 голосов
/ 20 апреля 2017

Следуя Кристиану Граусу 'в своем блоге «Вещи, которые я ненавижу в Microsoft» под названием « Path.Combine, по сути, бесполезен. », вот мое решение:

public static class Pathy
{
    public static string Combine(string path1, string path2)
    {
        if (path1 == null) return path2
        else if (path2 == null) return path1
        else return path1.Trim().TrimEnd(System.IO.Path.DirectorySeparatorChar)
           + System.IO.Path.DirectorySeparatorChar
           + path2.Trim().TrimStart(System.IO.Path.DirectorySeparatorChar);
    }

    public static string Combine(string path1, string path2, string path3)
    {
        return Combine(Combine(path1, path2), path3);
    }
}

Некоторые советуют, что пространства имен должны сталкиваться, ... Я пошел с Pathy, как небольшое, и чтобы избежать столкновения пространства имен с System.IO.Path.

Редактировать : добавлены проверки нулевых параметров

5 голосов
/ 10 сентября 2008

Не зная реальных деталей, я предполагаю, что он пытается присоединиться так, как если бы вы могли присоединиться к относительным URI. Например:

urljoin('/some/abs/path', '../other') = '/some/abs/other'

Это означает, что когда вы соединяете путь с предыдущей косой чертой, вы фактически соединяете одну базу с другой, и в этом случае вторая получает приоритет.

3 голосов
/ 15 мая 2018

Причина:

Ваш второй URL считается абсолютным путем. Метод Combine возвращает последний путь, только если последний путь является абсолютным путем.

Решение: Просто удалите начальный слеш / вашего второго Пути (/SecondPath до SecondPath). Тогда это работает, как вы исключили.

3 голосов
/ 03 декабря 2014

Этот код должен помочь:

        string strFinalPath = string.Empty;
        string normalizedFirstPath = Path1.TrimEnd(new char[] { '\\' });
        string normalizedSecondPath = Path2.TrimStart(new char[] { '\\' });
        strFinalPath =  Path.Combine(normalizedFirstPath, normalizedSecondPath);
        return strFinalPath;
2 голосов
/ 12 января 2017

Это на самом деле имеет смысл, в некотором смысле, учитывая, как обычно трактуются (относительные) пути:

string GetFullPath(string path)
{
     string baseDir = @"C:\Users\Foo.Bar";
     return Path.Combine(baseDir, path);
}

// Get full path for RELATIVE file path
GetFullPath("file.txt"); // = C:\Users\Foo.Bar\file.txt

// Get full path for ROOTED file path
GetFullPath(@"C:\Temp\file.txt"); // = C:\Temp\file.txt

Реальный вопрос: почему пути, начинающиеся с "\", считаются «корневыми»? Для меня это тоже было новым, но работает на Windows :

new FileInfo("\windows"); // FullName = C:\Windows, Exists = True
new FileInfo("windows"); // FullName = C:\Users\Foo.Bar\Windows, Exists = False
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...