Visual Studioのイミディエイトウィンドウから:
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
どちらも同じである必要があるようです。
古いFileSystemObject.BuildPath()はこのようには機能しませんでした...
これは一種の哲学的な質問です(おそらくMicrosoftだけが本当に答えることができます)。なぜなら、それはドキュメントに書かれていることを正確に実行しているからです。
「path2に絶対パスが含まれている場合、このメソッドはpath2を返します。」
.NETソースからの実際のCombineメソッドは次のとおりです。それが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;
}
理論的根拠がわかりません。解決策は、2番目のパスの先頭からDirectorySeparatorCharを削除(またはトリム)することだと思います。それを実行してからPath.Combine()を呼び出す独自の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 ReflectorforPath.Combineメソッドから分解されたコードです。IsPathRooted関数を確認してください。2番目のパスがルート化されている場合(DirectorySeparatorCharで始まる)、2番目のパスをそのまま返します。
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;
}
私の意見では、これはバグです。問題は、2 つの異なるタイプの「絶対」パスがあることです。パス "d:\mydir\myfile.txt" は絶対パスであり、パス "\mydir\myfile.txt" もドライブ文字がなくても "絶対パス" と見なされます。私の意見では、正しい動作は、2 番目のパスがディレクトリ セパレーターで始まる場合 (および UNC パスではない場合)、最初のパスのドライブ文字を先頭に追加することです。必要に応じて、希望する動作を持つ独自のヘルパー ラッパー関数を作成することをお勧めします。
MSDNから:
指定されたパスの1つが長さゼロの文字列である場合、このメソッドはもう1つのパスを返します。path2に絶対パスが含まれている場合、このメソッドはpath2を返します。
あなたの例では、path2は絶対です。
実際の詳細がわからないので、相対URIに参加するように参加しようとしていると思います。例えば:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
これは、前のスラッシュでパスを結合すると、実際には1つのベースを別のベースに結合していることを意味します。この場合、2番目が優先されます。
これは、(相対) パスが通常どのように扱われるかを考えると、実際には何らかの意味で理にかなっています。
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
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".
First, I evaluate if Path2 starts with / and if it is true, return Path2 without the first character. Otherwise, return the full Path2.
この \ は「現在のドライブのルート ディレクトリ」を意味します。あなたの例では、現在のドライブのルート ディレクトリにある "test" フォルダーを意味します。したがって、これは「c:\test」と等しくなります。
ライアンが述べたように、ドキュメントに記載されていることを正確に実行しています。
DOS 時から、現在のディスクと現在のパスが区別されます。
\
はルート パスですが、CURRENT DISK 用です。
すべての「ディスク」には、個別の「現在のパス」があります。を使用してディスクを変更する場合cd D:
、現在のパスを に変更するのではなく、次のように変更しますD:\
: "D:\whatever\was\the\last\path\accessed\on\this\disk"...
したがって、Windows では、文字通り@"\x"
「CURRENTDISK:\x」を意味します。したがってPath.Combine(@"C:\x", @"\y")
、既知のディスクではありませんが、相対パスではなくルートパスを2番目のパラメーターとして持ちます...そして、どれが«現在のディスク»であるかがわからないため、pythonは を返します"\\y"
。
>cd C:
>cd \mydironC\apath
>cd D:
>cd \mydironD\bpath
>cd C:
>cd
>C:\mydironC\apath
これら 2 つの方法により、区切り文字を含む 2 つの文字列を誤って結合することを防ぐことができます。
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);
}