在 Visual Studio的即时窗口中:
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
看来他们应该是一样的。
旧的 FileSystemObject.BuildPath() 不能这样工作......
这是一个哲学问题(也许只有微软才能真正回答),因为它完全按照文档所说的那样做。
“如果 path2 包含绝对路径,则此方法返回 path2。”
这是来自 .NET 源代码的实际组合方法。您可以看到它调用了CombineNoChecks,然后它在 path2 上调用IsPathRooted并返回该路径(如果是):
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;也许编写您自己的组合方法,然后调用 Path.Combine()。
我想解决这个问题:
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);
}
我还认为必须手动完成此字符串处理非常烦人,我会对这背后的原因感兴趣。
这是来自.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 路径)开头时,将驱动器号从第一个路径添加到前面。我建议编写自己的辅助包装函数,如果需要,它具有你想要的行为。
遵循Christian Graus在他的“我讨厌微软的事情”博客中的建议,标题为“ 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
.
编辑:添加空参数检查
这段代码应该可以解决问题:
string strFinalPath = string.Empty;
string normalizedFirstPath = Path1.TrimEnd(new char[] { '\\' });
string normalizedSecondPath = Path2.TrimStart(new char[] { '\\' });
strFinalPath = Path.Combine(normalizedFirstPath, normalizedSecondPath);
return strFinalPath;
不知道实际细节,我的猜测是它会尝试加入,就像你可能加入相对 URI 一样。例如:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
这意味着当您使用前面的斜杠连接路径时,实际上是将一个碱基连接到另一个碱基,在这种情况下,第二个碱基优先。
原因:
您的第二个 URL 被视为绝对路径,Combine
如果最后一个路径是绝对路径,该方法只会返回最后一个路径。
解决方案:
/
只需从第二个路径(/SecondPath
to )中删除前导斜杠SecondPath
,它就会像例外一样工作。
如果你想在不丢失任何路径的情况下合并两条路径,你可以使用这个:
?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。否则,返回完整的 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
我使用聚合函数来强制路径组合如下:
public class MyPath
{
public static string ForceCombine(params string[] paths)
{
return paths.Aggregate((x, y) => Path.Combine(x, y.TrimStart('\\')));
}
}
删除 Path.Combine 的第二个参数 (path2) 中的起始斜杠 ('\')。
这个\表示“当前驱动器的根目录”。在您的示例中,它表示当前驱动器根目录中的“测试”文件夹。因此,这可以等于“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);
}
正如 Ryan 所提到的,它完全按照文档所说的那样做。
从 DOS 时代来看,当前磁盘和当前路径是有区别的。
\
是根路径,但对于当前磁盘。
对于每个“磁盘”,都有一个单独的“当前路径”。如果您更改磁盘,cd D:
请不要将当前路径更改为D:\
,而是更改为: "D:\whatever\was\the\last\path\accessed\on\this\disk"...
因此,在 Windows 中,字面@"\x"
意思是:“CURRENTDISK:\x”。因此Path.Combine(@"C:\x", @"\y")
,第二个参数是根路径,而不是相对路径,虽然不在已知磁盘中......而且由于不知道哪个可能是«当前磁盘»,python返回"\\y"
。
>cd C:
>cd \mydironC\apath
>cd D:
>cd \mydironD\bpath
>cd C:
>cd
>C:\mydironC\apath