從Visual Studio中的即時窗口:Path.Combine爲什麼不正確連接以Path.DirectorySeparatorChar開頭的文件名?
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
他們似乎都應該是相同的。
老FileSystemObject.BuildPath()沒有以這種方式工作...
從Visual Studio中的即時窗口:Path.Combine爲什麼不正確連接以Path.DirectorySeparatorChar開頭的文件名?
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
他們似乎都應該是相同的。
老FileSystemObject.BuildPath()沒有以這種方式工作...
這是一個哲學問題(可能只有微軟才能真正回答),因爲它正在完成文檔所說的內容。
「如果PATH2包含一個絕對路徑,這個方法返回路徑2」。
Here's the actual Combine method from .NET source。你可以看到它調用CombineNoChecks,然後在路徑2上調用IsPathRooted,如果是,則返回該路徑。
我不知道理由是什麼。我想解決方案是從第二條路徑的開始剝離(或修剪)DirectorySeparatorChar;也許寫你自己的Combine方法,然後調用Path.Combine()。
反彙編代碼(查看我的文章),你是對的。 – 2008-09-10 00:25:32
不知道實際的細節,我的猜測是,它使一個嘗試加入像你可能會加入相關的URI。例如:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
這意味着,當你加入一個路徑與前面的斜線時,實際上是連接一個基站到另一個,在此情況下,第二獲取優先級。
這是Path.Combine方法的.NET Reflector的反彙編代碼。檢查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路徑)時,從第一個路徑預先安裝驅動器號。我會建議編寫你自己的幫助包裝函數,如果你需要它有你想要的行爲。
此\表示「當前驅動器的根目錄」。在您的示例中,它表示當前驅動器根目錄中的「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.Substring(0, 1) == @"\" ? Path2.Substring(1, Path2.Length - 1) : Path2);
:那麼,這可以用「\測試C」等於這兩種情況都會返回「C:\ test \ test」。
首先,我評估Path2是否以/開頭,如果是,則返回沒有第一個字符的Path2。否則,返回完整路徑2。
此代碼應該做的伎倆:
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
真正的問題是,爲什麼與"\"
作爲被認爲是「根」開始的路徑。這是新的給我,但it works that way on windows:
new FileInfo("\windows"); // FullName = C:\Windows, Exists = True
new FileInfo("windows"); // FullName = C:\Users\Foo.Bar\Windows, Exists = False
在他的‘事情我討厭微軟’繼Christian Graus的意見博客名爲‘Path.Combine is essentially useless.’,這裏是我的解決方案:
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);
}
OMG這太愚蠢了,它「工作」這種方式。 – Joe 2011-08-19 18:30:03
[它仍然不會在.NET核心中改變。](https://github.com/dotnet/coreclr/blob/fb86c0294a999b2c7bd1e13da1fdc0d3c2f701e5/src/mscorlib/shared/System/IO/Path.cs#L189) – zwcloud 2017-04-12 15:43:40
@Joe ,愚蠢是對的!另外,我必須指出[等效函數](https://nodejs.org/api/path.html#path_path_join_paths)在Node.JS中工作得很好......在微軟上搖頭...... – 2017-09-06 18:19:42