How to ensure there is trailing directory separator in paths?

Posted on

Problem :

I’m having an issue with AppDomain.CurrentDomain.BaseDirectory.

Sometimes the path ends with ”, sometimes it doesn’t and I can’t find a reason for this.

It would be fine if I was using Path.Combine but I want to do Directory.GetParent and it yields different results.

My current solution is:

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

Is there another way to get the parent directory of the application?

Solution :

You can easily ensure the behaviour you desire by using TrimEnd:

var baseDir = AppDomain.CurrentDomain.BaseDirectory
                  .TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;

To be optimally efficient (by avoiding extra allocations), check that the string doesn’t end with a before making changes, since you won’t always need to:

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;

It’s like that, just keep your hack.

In plain Win32 there is an helper function PathAddBackslash for that. Just be consistent with directory separator: check Path.DirectorySeparatorChar and Path.AltDirectorySeparatorChar instead of hard-code .

Something like this (please note there is not a serious error checking):

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:pathfilename.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;

Why so much code? Primary because if user enter /windows/system32 you don’t want to get /windows/system32 but /windows/system32/, devil is in the details…

To put everything together in a nicer self-explicative form:

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;

URI format file:// is not handled even if it may seem so. The right thing is again to do what the other .NET I/O functions do: do not handle this format (and possibly throw an exception).

As alternative you’re always able to import Win32 function:

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

I often use

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

Or, if I needed this more than once or twice in the same project, I’d probably use a helper function like this:

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];
        return path + Path.DirectorySeparatorChar;

In order to get cross platform support one can use this snippet:

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;

As a method:

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

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

Or as an extension method:

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

As of .NET Core 3.0, Path.EndsInDirectorySeparator() can be used:

string baseDir = AppDomain.CurrentDomain.BaseDirectory;

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

For Unix, it checks if the last char is '/'.

For Windows it checks if the last char is a literal '' or '/'.

You can simply use the C# Path library, there is a method named TrimEndingDirectorySeparator, give your path to the method, if any directory seperator exist end of your path it will trimed, no matter your path is virtual or physical, then instead of using old way to make path string with String.Format use C# $ key, if you have to use / seperator in your path simply mix $ and @ as like as below example.

var baseDir = AppDomain.CurrentDomain.BaseDirectory;

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

Leave a Reply

Your email address will not be published. Required fields are marked *