如何确保路径中存在尾部目录分隔符?

59

我正在使用 AppDomain.CurrentDomain.BaseDirectory 时遇到问题。

有时路径末尾会以 '' 结尾,而有时则没有,我找不到原因。

如果我使用 Path.Combine 就没问题了,但是我想要使用 Directory.GetParent ,结果却不同。

我的当前解决方案是:

var baseDir = AppDomain.CurrentDomain.BaseDirectory;
if (!baseDir.EndsWith("\\")) baseDir += "\\";

有没有其他方法来获取应用程序的父目录?

7个回答

72

您可以使用 TrimEnd 轻松实现所需的行为:

var baseDir = AppDomain.CurrentDomain.BaseDirectory
                  .TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
为了达到最佳效率(避免额外的分配),在进行更改之前,请检查字符串是否以\结尾,因为你并不总是需要它:

为了达到最佳效率(避免额外的分配),在进行更改之前,请检查字符串是否以\结尾,因为你并不总是需要它:

const string sepChar = Path.DirectorySeparatorChar.ToString();
const string altChar = Path.AltDirectorySeparatorChar.ToString();

var baseDir = AppDomain.CurrentDomain.BaseDirectory;
if (!baseDir.EndsWith(sepChar) && !baseDir.EndsWith(altChar))
{
    baseDir += sepChar;
}

27
为了使其跨平台,您可以将 "\\" 替换为 Path.DirectorySeparatorChar - Rots
7
“TrimEnd”调用应该包括“AltDirectorySeparatorChar”。 - Paul Groke
使用三元运算符略微简洁的语法:var baseDir = AppDomain.CurrentDomain.BaseDirectory;baseDir = baseDir.EndsWith(Path.DirectorySeparatorChar.ToString()) ? baseDir : baseDir + Path.DirectorySeparatorChar; - ewilan

46

就像这样,保持你的黑客。

在普通的Win32中,有一个辅助函数PathAddBackslash可以实现这个功能。只需与目录分隔符保持一致:检查Path.DirectorySeparatorCharPath.AltDirectorySeparatorChar而不是硬编码\

更新:如果您的目标是任何较新的.NET版本(Core 3.0及以上),则可以使用一些较新的函数:

string PathAddDirectorySeparator(string path)
{
    if (path is null)
        throw new ArgumentNullException(nameof(path));

    path = path.TrimEnd();

    if (Path.EndsInDirectorySeparator(path))
        return path;

    return path + GetDirectorySeparatorUsedInPath();

    char GetDirectorySeparatorUsedInPath()
    {
        if (path.Contains(Path.AltDirectorySeparatorChar))
            return Path.AltDirectorySeparatorChar;
       
        return Path.DirectorySeparatorChar;
   }
}

如果你决定不支持 Path.AltDirectorySeparatorChar,并且你不介意额外的字符串分配,那么考虑使用 Path.TrimEndingDirectorySeparator(path) 而不是简单地使用 path.TrimEnd('\\')

原始答案:类似这样(请注意没有严格的错误检查):

string PathAddBackslash(string path)
{
    // They're always one character but EndsWith is shorter than
    // array style access to last path character. Change this
    // if performance are a (measured) issue.
    string separator1 = Path.DirectorySeparatorChar.ToString();
    string separator2 = Path.AltDirectorySeparatorChar.ToString();

    // Trailing white spaces are always ignored but folders may have
    // leading spaces. It's unusual but it may happen. If it's an issue
    // then just replace TrimEnd() with Trim(). Tnx Paul Groke to point this out.
    path = path.TrimEnd();

    // Argument is always a directory name then if there is one
    // of allowed separators then I have nothing to do.
    if (path.EndsWith(separator1) || path.EndsWith(separator2))
        return path;

    // If there is the "alt" separator then I add a trailing one.
    // Note that URI format (file://drive:\path\filename.ext) is
    // not supported in most .NET I/O functions then we don't support it
    // here too. If you have to then simply revert this check:
    // if (path.Contains(separator1))
    //     return path + separator1;
    //
    // return path + separator2;
    if (path.Contains(separator2))
        return path + separator2;

    // If there is not an "alt" separator I add a "normal" one.
    // It means path may be with normal one or it has not any separator
    // (for example if it's just a directory name). In this case I
    // default to normal as users expect.
    return path + separator1;
}

为什么需要这么多代码?主要是因为,如果用户输入/windows/system32,你不希望得到/windows/system32\,而是/windows/system32/。关键在于细节......

为了更好地自我解释,将所有内容整合在一起:

string PathAddBackslash(string path)
{
    if (path == null)
        throw new ArgumentNullException(nameof(path));

    path = path.TrimEnd();

    if (PathEndsWithDirectorySeparator())
        return path;

    return path + GetDirectorySeparatorUsedInPath();

    bool PathEndsWithDirectorySeparator()
    {
        if (path.Length == 0)
            return false;

        char lastChar = path[path.Length - 1];
        return lastChar == Path.DirectorySeparatorChar
            || lastChar == Path.AltDirectorySeparatorChar;
    }

    char GetDirectorySeparatorUsedInPath()
    {
        if (path.Contains(Path.AltDirectorySeparatorChar))
            return Path.AltDirectorySeparatorChar;
    
        return Path.DirectorySeparatorChar;
    }
}

即使看起来如此,file:// URI格式也不被处理。正确的做法是像其他.NET I/O函数一样:不要处理此格式(并可能引发异常)。

作为替代方案,您始终可以导入Win32函数:

[DllImport("shlwapi.dll", 
    EntryPoint = "PathAddBackslashW",
    SetLastError = True,
    CharSet = CharSet.Unicode)]
static extern IntPtr PathAddBackslash(
    [MarshalAs(UnmanagedType.LPTStr)]StringBuilder lpszPath);

我不确定我会使用这个替代方案。虽然它很有趣,但开发人员可能不确定代码在做什么。 - pitermarx
5
@pitermarx,实际上我们甚至不确定FileCopy到底做了什么!在我看来,选择一个系统函数总是一个好选择,至少它会为您处理任何更改(并且它总是遵循其合约:添加尾部反斜杠)。当然,并非总是完美解决方案…… - Adriano Repetti
@AdrianoRepetti:Windows允许在文件/目录名称前面添加空格,因此Trim()调用没有任何作用。(它可能也允许在末尾添加空格,但至少cmd.exe似乎不支持它们,我还没有进行进一步的测试,所以不能确定。) 个人而言,我会完全删除Trim调用。毕竟,函数的责任不是“修复”损坏的路径。 另外,我会将“添加哪一个”的逻辑更改为仅复制字符串中的最后一个分隔符(我见过在其余字符串中使用反斜杠的file://路径)。 - Paul Groke
@PaulGroke 你说得对,关于前导空格的问题已经更新了答案。在大多数.NET I/O函数中,URI格式是不被允许的,所以这不是个问题(在处理文件://类型的URI时,您必须自己提取路径)。 - Adriano Repetti
你的第一个版本是否将 file://drive:\path\filename.ext 转换为 file://drive:\path\filename.ext/,而第二个版本则创建了 file://drive:\path\filename.ext\?也就是说,如果有混合分隔符,第一个版本不会优先选择备选分隔符,而第二个版本则更喜欢标准分隔符? - Sebastian Mach
@SebastianMach 是的,我在评论中写了:URI格式不受支持(与大多数.NET I/O函数一样)。_修复_也在同一条评论中:反转检查(file://drive:\path1\path2将变为file://drive:\path1\path2\)。第二个版本已经这样做了。但请注意,还有更多的边角情况需要考虑(file://path怎么办?),那么正确的做法就是像.NET库一样忽略该格式。 - Adriano Repetti

7

我经常使用

path = Path.Combine(path, "x");
path = path.Substring(0, path.Length - 1);

或者,如果我在同一个项目中需要使用这个功能一次以上,我可能会使用像这样的帮助函数:

string EnsureTerminatingDirectorySeparator(string path)
{
    if (path == null)
        throw new ArgumentNullException("path");

    int length = path.Length;
    if (length == 0)
        return "." + Path.DirectorySeparatorChar;

    char lastChar = path[length - 1];
    if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar)
        return path;

    int lastSep = path.LastIndexOfAny(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
    if (lastSep >= 0)
        return path + path[lastSep];
    else
        return path + Path.DirectorySeparatorChar;
}

6
为了获得跨平台支持,可以使用以下代码片段:
using System.IO;

// Your input string.
string baseDir = AppDomain.CurrentDomain.BaseDirectory;

// Get the absolut path from it (in case ones input is a relative path).
string fullPath = Path.GetFullPath(baseDir);

// Check for ending slashes, remove them (if any)
// and add a cross platform slash at the end.
string result = fullPath
                    .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
                    + Path.DirectorySeparatorChar;

作为一种方法:

private static string GetFullPathWithEndingSlashes(string input)
{
    string fullPath = Path.GetFullPath(input);

    return fullPath
        .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
        + Path.DirectorySeparatorChar;
}

作为扩展方法:

或者作为一个扩展方法:

public static string GetFullPathWithEndingSlashes(this string input)
{
    return Path.GetFullPath(input)
        .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
        + Path.DirectorySeparatorChar;
}

6
从.NET Core 3.0开始,可以使用Path.EndsInDirectorySeparator()
string baseDir = AppDomain.CurrentDomain.BaseDirectory;

if (!Path.EndsInDirectorySeparator(baseDir))
{
    baseDir += Path.DirectorySeparatorChar;
}

对于Unix,它检查最后一个char是否为'/'

对于Windows,它检查最后一个char是否为字面上的'\''/'


0

如果你无论如何都使用Path.Combine,那么

var result = Path.Combine(basepath, subpath, " ").Trim();

可能是一个简短的解决方案,而不是这个非常长的TrimEndingDirectorySeparator。 注意最后一个参数是空格。

0
你可以简单地使用C# Path库,其中有一个名为TrimEndingDirectorySeparator的方法,将您的路径传递给该方法,如果您的路径末尾存在任何目录分隔符,则会被修剪,无论您的路径是虚拟的还是物理的,然后不要再使用旧的方式来制作路径字符串,而是使用C# $键,如果您必须在路径中使用/分隔符,只需像下面的示例一样混合$和@即可。
var baseDir = AppDomain.CurrentDomain.BaseDirectory;

string finalPath = $@"{Path.TrimEndingDirectorySeparator(baseDir)}/"

1
如果您知道路径应该采用哪种格式,即如果您知道斜杠的方向,那么这是一个不错的简单解决方案 - 其他一些解决方案可能更长,但在这方面提供了更多的灵活性/适应性。此外,为了让其他人清楚,这仅适用于.NET Core 3.0和.NET 5.0及以上版本。https://learn.microsoft.com/en-us/dotnet/api/system.io.path.trimendingdirectoryseparator?view=net-5.0 - Robert Shattock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接