Path.Combine이 Path.DirectorySeparatorChar로 시작하는 파일 이름을 제대로 연결하지 못하는 이유는 무엇입니까?
문제
로부터 직접 실행 창 비주얼 스튜디오에서:
> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"
둘 다 동일해야 할 것 같습니다.
이전 FileSystemObject.BuildPath()는 이런 방식으로 작동하지 않았습니다.
해결책
이것은 문서에 명시된 것과 정확히 일치하기 때문에 일종의 철학적인 질문입니다(아마도 Microsoft만이 진정으로 답변할 수 있습니다).
"path2에 절대 경로가 포함된 경우 이 메서드는 path2를 반환합니다."
실제 Combine 메소드는 다음과 같습니다. .NET 소스에서.호출되는 것을 볼 수 있습니다. CombineNoChecks, 그러면 다음이 호출됩니다. IsPathRooted path2에서 해당 경로를 반환합니다.
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()을 호출하는 자체 Combine 메서드를 작성할 수도 있습니다.
다른 팁
이것은 디스어셈블된 코드입니다. .NET 리플렉터 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;
}
나는 이 문제를 해결하고 싶었습니다:
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);
}
또한 이 문자열 처리를 수동으로 수행해야 한다는 것이 상당히 짜증나는 일이라고 생각하며, 그 이유에 관심이 있습니다.
제 생각에는 이것은 버그입니다.문제는 "절대" 경로에는 두 가지 유형이 있다는 것입니다."d:\mydir\myfile.txt" 경로는 절대 경로이고, "\mydir\myfile.txt" 경로도 드라이브 문자가 누락된 경우에도 "절대" 경로로 간주됩니다.제 생각에는 두 번째 경로가 디렉터리 구분 기호로 시작하고 UNC 경로가 아닌 경우 첫 번째 경로의 드라이브 문자를 앞에 추가하는 것이 올바른 동작이라고 생각합니다.필요한 경우 원하는 동작을 수행하는 도우미 래퍼 함수를 직접 작성하는 것이 좋습니다.
에서 MSDN:
지정된 경로 중 하나가 길이가 0인 문자열인 경우 이 메서드는 다른 경로를 반환합니다.path2에 절대 경로가 포함된 경우 이 메서드는 path2를 반환합니다.
귀하의 예에서 path2는 절대적입니다.
수행원 크리스티안 그라우스'라는 제목의 그의 "내가 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
.
편집하다:null 매개변수 검사가 추가되었습니다.
실제 세부 사항을 알지 못하는 경우 상대 URI에 조인하는 것처럼 조인을 시도하는 것 같습니다.예를 들어:
urljoin('/some/abs/path', '../other') = '/some/abs/other'
즉, 앞에 슬래시가 있는 경로를 연결하면 실제로 한 기반을 다른 기반에 연결하게 되며, 이 경우 두 번째 기반이 우선 적용됩니다.
이 코드는 트릭을 수행해야 합니다.
string strFinalPath = string.Empty;
string normalizedFirstPath = Path1.TrimEnd(new char[] { '\\' });
string normalizedSecondPath = Path2.TrimStart(new char[] { '\\' });
strFinalPath = Path.Combine(normalizedFirstPath, normalizedSecondPath);
return strFinalPath;
이유:
두 번째 URL은 절대 경로로 간주됩니다. Combine
메소드는 마지막 경로가 절대 경로인 경우에만 마지막 경로를 반환합니다.
해결책: 시작 슬래시를 제거하십시오. /
두 번째 경로(/SecondPath
에게 SecondPath
).그러면 당신이 제외하고 작동합니다.
이는 (상대) 경로가 일반적으로 처리되는 방식을 고려하면 어떤 면에서는 실제로 의미가 있습니다.
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 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);
}
\는 "현재 드라이브의 루트 디렉터리"를 의미합니다.귀하의 예에서는 현재 드라이브의 루트 디렉터리에 있는 "test" 폴더를 의미합니다.따라서 이는 "c: est"와 동일할 수 있습니다.
경로를 잃지 않고 두 경로를 결합하려면 다음을 사용할 수 있습니다.
?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: est est"를 반환합니다.
먼저 Path2가 /로 시작하는지 평가하고, true이면 첫 번째 문자 없이 Path2를 반환합니다.그렇지 않으면 전체 Path2를 반환합니다.