C# 为什么 Path.Combine 不能正确连接以 Path.DirectorySeparatorChar 开头的文件名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/53102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does Path.Combine not properly concatenate filenames that start with Path.DirectorySeparatorChar?
提问by Kris Erickson
From the Immediate Windowin Visual Studio:
从Visual Studio 中的立即窗口:
> Path.Combine(@"C:\x", "y")
"C:\x\y"
> Path.Combine(@"C:\x", @"\y")
"\y"
It seems that they should both be the same.
似乎两者应该是一样的。
The old FileSystemObject.BuildPath() didn't work this way...
旧的 FileSystemObject.BuildPath() 不能这样工作......
采纳答案by Ryan Lundy
This is kind of a philosophical question (which perhaps only Microsoft can truly answer), since it's doing exactly what the documentation says.
这是一个哲学问题(也许只有 Microsoft 才能真正回答),因为它完全按照文档中的说明进行操作。
"If path2 contains an absolute path, this method returns path2."
“如果 path2 包含绝对路径,则此方法返回 path2。”
Here's the actual Combine methodfrom the .NET source. You can see that it calls CombineNoChecks, which then calls IsPathRootedon path2 and returns that path if so:
这是来自 .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;
}
I don't know what the rationale is. I guess the solution is to strip off (or Trim) DirectorySeparatorChar from the beginning of the second path; maybe write your own Combine method that does that and then calls Path.Combine().
我不知道这是什么原理。我想解决方案是从第二条路径的开头剥离(或修剪)DirectorySeparatorChar;也许编写自己的Combine 方法来执行此操作,然后调用Path.Combine()。
回答by elarson
Not knowing the actual details, my guess is that it makes an attempt to join like you might join relative URIs. For example:
不知道实际的细节,我的猜测是它尝试加入,就像你可能加入相对 URI 一样。例如:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
This means that when you join a path with a preceding slash, you are actually joining one base to another, in which case the second gets precedence.
这意味着当您使用前面的斜杠连接路径时,您实际上是将一个基础连接到另一个基础,在这种情况下,第二个获得优先权。
回答by nickd
回答by Gulzar Nazim
This is the disassembled code from .NET Reflectorfor Path.Combine method. Check IsPathRooted function. If the second path is rooted (starts with a DirectorySeparatorChar), return second path as it is.
这是.NET Reflectorfor 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;
}
回答by Wedge
In my opinion this is a bug. The problem is that there are two different types of "absolute" paths. The path "d:\mydir\myfile.txt" is absolute, the path "\mydir\myfile.txt" is also considered to be "absolute" even though it is missing the drive letter. The correct behavior, in my opinion, would be to prepend the drive letter from the first path when the second path starts with the directory separator (and is not a UNC path). I would recommend writing your own helper wrapper function which has the behavior you desire if you need it.
在我看来,这是一个错误。问题是有两种不同类型的“绝对”路径。路径“d:\mydir\myfile.txt”是绝对路径,路径“\mydir\myfile.txt”也被认为是“绝对”路径,即使它缺少驱动器号。在我看来,正确的行为是当第二个路径以目录分隔符(并且不是 UNC 路径)开头时,在第一个路径之前添加驱动器号。我建议您编写自己的辅助包装函数,如果需要,该函数具有您想要的行为。
回答by Estevez
This \ means "the root directory of the current drive". In your example it means the "test" folder in the current drive's root directory. So, this can be equal to "c:\test".
这个\ 表示“当前驱动器的根目录”。在您的示例中,它表示当前驱动器根目录中的“test”文件夹。因此,这可以等于“c:\test”。
回答by Ferri
If you want to combine both paths without losing any path you can use this:
如果您想在不丢失任何路径的情况下组合两条路径,您可以使用以下命令:
?Path.Combine(@"C:\test", @"\test".Substring(0, 1) == @"\" ? @"\test".Substring(1, @"\test".Length - 1) : @"\test");
Or with variables:
或者使用变量:
string Path1 = @"C:\Test";
string Path2 = @"\test";
string FullPath = Path.Combine(Path1, Path2.IsRooted() ? Path2.Substring(1, Path2.Length - 1) : Path2);
Both cases return "C:\test\test".
这两种情况都返回“C:\test\test”。
First, I evaluate if Path2 starts with / and if it is true, return Path2 without the first character. Otherwise, return the full Path2.
首先,我评估 Path2 是否以 / 开头,如果为真,则返回不带第一个字符的 Path2。否则,返回完整的 Path2。
回答by The King
This code should do the trick:
这段代码应该可以解决问题:
string strFinalPath = string.Empty;
string normalizedFirstPath = Path1.TrimEnd(new char[] { '\' });
string normalizedSecondPath = Path2.TrimStart(new char[] { '\' });
strFinalPath = Path.Combine(normalizedFirstPath, normalizedSecondPath);
return strFinalPath;
回答by anhoppe
I wanted to solve this problem:
我想解决这个问题:
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);
Of course, all paths 1-9 should contain an equivalent string in the end. Here is the PathCombine method I came up with:
当然,所有路径 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);
}
I also think that it is quite annoying that this string handling has to be done manually, and I'd be interested in the reason behind this.
我还认为必须手动完成此字符串处理很烦人,我对这背后的原因很感兴趣。
回答by marsze
This actually makes sense, in some way, considering how (relative) paths are treated usually:
在某种程度上,考虑到通常如何处理(相对)路径,这实际上是有道理的:
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
The real question is: Why are paths, which start with "\"
, considered "rooted"? This was new to me too, but it works that way on Windows:
真正的问题是:为什么以 开头的路径"\"
被认为是“有根的”?这对我来说也是新的,但它在 Windows 上是这样工作的:
new FileInfo("\windows"); // FullName = C:\Windows, Exists = True
new FileInfo("windows"); // FullName = C:\Users\Foo.Bar\Windows, Exists = False