В окне Immediate в Visual Studio:
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
Кажется, что они должны быть одинаковыми.
Старый FileSystemObject.BuildPath() не работал таким образом...
В окне Immediate в Visual Studio:
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
Кажется, что они должны быть одинаковыми.
Старый FileSystemObject.BuildPath() не работал таким образом...
Это своего рода философский вопрос (на который, пожалуй, может ответить только Microsoft), поскольку он делает именно то, что говорится в документации.
"Если 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().
Это дизассемблированный код из .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;
}
Я хотел решить эту проблему:
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);
}
Я также думаю, что довольно досадно, что эту обработку строк нужно выполнять вручную, и мне было бы интересно узнать причину этого.
По-моему, это ошибка. Проблема в том, что существуют два разных типа "абсолютных" путей. Путь "d:\mydir\myfile.txt" является абсолютным, путь "\ mydir\myfile.txt" также считается "абсолютным", даже если ему не хватает буквы диска. Правильное поведение, на мой взгляд, состояло бы в том, чтобы дописать букву диска от первого пути, когда второй путь начинается с разделителя каталогов (и не является UNC-контуром). Я бы рекомендовал написать свою собственную вспомогательную функцию-обертку, которая имеет желаемое поведение, если вам это нужно.
От MSDN:
Если один из указанных путей является строкой нулевой длины, этот метод возвращает другой путь. Если path2 содержит абсолютный путь, этот метод возвращает path2.
В вашем примере path2 является абсолютным.
Следуя Christian Graus "совет в своем блоге" Things я Hate about 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
.
Изменить: добавлены проверки нулевого параметра
Не зная фактических данных, я предполагаю, что он пытается присоединиться, например, вы можете присоединиться к относительным URI. Например:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
Это означает, что, когда вы присоединяетесь к пути с предыдущей косой чертой, вы фактически соединяете одну базу с другой, и в этом случае вторая имеет приоритет.
Этот код должен сделать трюк:
string strFinalPath = string.Empty;
string normalizedFirstPath = Path1.TrimEnd(new char[] { '\\' });
string normalizedSecondPath = Path2.TrimStart(new char[] { '\\' });
strFinalPath = Path.Combine(normalizedFirstPath, normalizedSecondPath);
return strFinalPath;
Причина:
Ваш второй URL считается абсолютным путем. Метод Combine
возвращает последний путь, только если последний путь является абсолютным путем.
Решение: Просто удалите начальный слеш /
вашего второго Пути (/SecondPath
- SecondPath
). Тогда это работает, как вы исключили.
Это на самом деле имеет смысл, в некотором смысле, учитывая, как обычно обрабатываются (относительные) пути:
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
Если вы хотите объединить оба пути без потери пути, вы можете использовать это:
?Path.Combine(@"C:\test", @"\test".Substring(0, 1) == @"\" ? @"\test".Substring(1, @"\test".Length - 1) : @"\test");
Или с переменными:
string Path1 = @"C:\Test";
string Path2 = @"\test";
string FullPath = Path.Combine(Path1, Path2.Substring(0, 1) == @"\" ? Path2.Substring(1, Path2.Length - 1) : Path2);
В обоих случаях возвращается "C:\test\test".
Сначала я оцениваю, начинается ли Path2 с /, и если оно истинно, возвращаю Path2 без первого символа. В противном случае верните полный путь2.
Это\означает "корневой каталог текущего диска". В вашем примере это означает "тестовую" папку в текущем корневом каталоге диска. Таким образом, это может быть равно "c:\test".
Эти два метода должны спасти вас от случайного объединения двух строк, в которых оба имеют разделитель.
public static string Combine(string x, string y, char delimiter) {
return $"{ x.TrimEnd(delimiter) }{ delimiter }{ y.TrimStart(delimiter) }";
}
public static string Combine(string[] xs, char delimiter) {
if (xs.Length < 1) return string.Empty;
if (xs.Length == 1) return xs[0];
var x = Combine(xs[0], xs[1], delimiter);
if (xs.Length == 2) return x;
var ys = new List<string>();
ys.Add(x);
ys.AddRange(xs.Skip(2).ToList());
return Combine(ys.ToArray(), delimiter);
}
Удалите начальную косую черту ('\') во втором параметре (path2) файла Path.Combine.
Я использовал агрегатную функцию для объединения путей, как показано ниже:
public class MyPath
{
public static string ForceCombine(params string[] paths)
{
return paths.Aggregate((x, y) => Path.Combine(x, y.TrimStart('\\')));
}
}