Из Немедленное окно в Visual Studio:
> Path.Combine(@"C:\x", "y")
"C:\x\y"
> Path.Combine(@"C:\x", @"\y")
"\y"
Кажется, что они оба должны быть одинаковыми.
Старый FileSystemObject.BuildPath () так не работал ...
@ Джо, глупый прав! Кроме того, я должен отметить, что эквивалентная функция отлично работает в Node.JS ... Качая головой в Microsoft ...
@zwcloud Для .NET Core / Standard Path.Combine() в основном предназначен для обратной совместимости (с существующим поведением). Вам лучше использовать Path.Join(): «В отличие от метода Combine, метод Join не пытается укоренить возвращаемый путь. (То есть, если path2 является абсолютным путем, метод Join не отбрасывает path1 и не возвращает path2, как это делает метод Combine.)»





Не зная фактических деталей, я предполагаю, что он пытается присоединиться, как если бы вы могли присоединиться к относительным URI. Например:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
Это означает, что когда вы присоединяетесь к пути с предыдущей косой чертой, вы фактически присоединяете одну базу к другой, и в этом случае вторая получает приоритет.
Я думаю, что косые черты следует пояснить. Кроме того, какое отношение это имеет к .NET?
От MSDN:
If one of the specified paths is a zero-length string, this method returns the other path. If path2 contains an absolute path, this method returns path2.
В вашем примере path2 является абсолютным.
Это своего рода философский вопрос (на который, возможно, только Microsoft может по-настоящему ответить), поскольку она делает именно то, что говорится в документации.
«Если путь2 содержит абсолютный путь, этот метод возвращает путь2».
Вот собственно метод Combine из источника .NET. Вы можете видеть, что он вызывает CombineNoChecks, который затем вызывает IsPathRooted на пути 2 и возвращает этот путь, если это так:
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 ().
Глядя на дизассемблированный код (проверьте мой пост), вы в чем-то правы.
Я предполагаю, что это работает таким образом, чтобы обеспечить легкий доступ к алгоритму «текущего рабочего каталога».
Это похоже на выполнение последовательности cd (component) из командной строки. Для меня это звучит разумно.
Я использую эту обрезку, чтобы получить желаемую строку эффекта strFilePath = Path.Combine (basePath, otherPath.TrimStart (new char [] {'\\', '/'}));
Я изменил свой рабочий код на 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;
}
На мой взгляд это ошибка. Проблема в том, что существует два разных типа «абсолютных» путей. Путь «d: \ mydir \ myfile.txt» является абсолютным, путь «\ mydir \ myfile.txt» также считается «абсолютным», даже если в нем отсутствует буква диска. На мой взгляд, правильным поведением будет добавление буквы диска к первому пути, когда второй путь начинается с разделителя каталогов (и не является UNC-путем). Я бы порекомендовал написать вашу собственную вспомогательную функцию-оболочку, которая будет иметь желаемое поведение, если вам это нужно.
Он соответствует спецификации, но это не то, чего я ожидал.
@Jake Это не позволяет избежать исправления; это несколько людей, думая долго и упорно о том, как сделать что-то, а затем придерживаться, что они согласны. Также обратите внимание на разницу между .Net framework (библиотека, содержащая Path.Combine) и языком C#.
Это \ означает "корневой каталог текущего диска". В вашем примере это означает «тестовую» папку в корневом каталоге текущего диска. Таким образом, это может быть равно «c: \ test».
Если вы хотите объединить оба пути без потери пути, вы можете использовать это:
?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.IsRooted() ? Path2.Substring(1, Path2.Length - 1) : Path2);
В обоих случаях возвращается «C: \ test \ test».
Сначала я оцениваю, начинается ли Path2 с /, и если это правда, возвращаю Path2 без первого символа. В противном случае верните полный путь 2.
Вероятно, безопаснее заменить проверку == @"\" вызовом Path.IsRooted(), поскольку "\" - не единственный персонаж, который нужно учитывать.
Этот код должен помочь:
string strFinalPath = string.Empty;
string normalizedFirstPath = Path1.TrimEnd(new char[] { '\' });
string normalizedSecondPath = Path2.TrimStart(new char[] { '\' });
strFinalPath = Path.Combine(normalizedFirstPath, normalizedSecondPath);
return strFinalPath;
Я хотел решить эту проблему:
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);
}
Я также думаю, что это довольно раздражает, что эту обработку строки приходится выполнять вручную, и меня бы интересовала причина этого.
Это действительно имеет смысл, учитывая, как обычно обрабатываются (относительные) пути:
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
Следуя совету Кристиан Граус в его блоге «Что я ненавижу в 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.
Редактировать: добавлены проверки нулевого параметра
Эти два метода должны уберечь вас от случайного соединения двух строк, в каждой из которых есть разделитель.
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);
}
Причина:
Ваш второй URL-адрес считается абсолютным путем. Метод Combine вернет только последний путь, если последний путь является абсолютным.
Решение: Просто удалите начальную косую черту / вашего второго пути (от /SecondPath до SecondPath). Тогда он работает так, как вы исключили.
Удалите начальную косую черту ('\') во втором параметре (path2) Path.Combine.
Вопрос не в этом.
Я использовал агрегатную функцию, чтобы объединить пути, как показано ниже:
public class MyPath
{
public static string ForceCombine(params string[] paths)
{
return paths.Aggregate((x, y) => Path.Combine(x, y.TrimStart('\')));
}
}
Как упоминал Райан, он делает именно то, что говорится в документации.
От времен DOS различаются текущий диск и текущий путь.
\ - это корневой путь, но для ТЕКУЩЕГО ДИСКА.
Для каждого «диск» есть отдельный «текущий путь».
Если вы меняете диск с помощью cd D:, вы меняете не текущий путь на D:\, а на: «D: \ something \ was \ the \ last \ path \ accessed \ on \ this \ disk» ...
Итак, в Windows буквальный @"\x" означает: «ТЕКУЩИЙ ДИСК: \ x».
Следовательно, Path.Combine(@"C:\x", @"\y") имеет в качестве второго параметра корневой путь, а не относительный, хотя и не на известном диске ...
А поскольку неизвестно, какой может быть «текущий диск», python возвращает "\y".
>cd C:
>cd \mydironC\apath
>cd D:
>cd \mydironD\bpath
>cd C:
>cd
>C:\mydironC\apath