Path.Combine이 Path.DirectorySeparatorChar로 시작하는 파일 이름을 제대로 연결하지 못하는 이유는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/53102

  •  09-06-2019
  •  | 
  •  

문제

로부터 직접 실행 창 비주얼 스튜디오에서:

> Path.Combine(@"C:\x", "y")
"C:\\x\\y"
> Path.Combine(@"C:\x", @"\y")
"\\y"

둘 다 동일해야 할 것 같습니다.

이전 FileSystemObject.BuildPath()는 이런 방식으로 작동하지 않았습니다.

도움이 되었습니까?

해결책

이것은 문서에 명시된 것과 정확히 일치하기 때문에 일종의 철학적인 질문입니다(아마도 Microsoft만이 진정으로 답변할 수 있습니다).

시스템.IO.경로.결합

"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를 반환합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top