Skip to content

Add Path.RemoveRelativeSegments Api #2162

@Anipik

Description

@Anipik

Edit by carlossanlop: I marked this issue as up-for-grabs. If you're interested in taking it, please read this comment. This feature is fully implemented already, but the only thing pending to fix is a Unix performance regression before merging.

Rationale

Currently there is no direct way to normalize the path or to remove the relative Segments from any path string. We can use GetFullPath to normalize the path but it will resolve it relative to root directory or working directory which is not always desired.
eg

  • working with relative paths in archives where we may require normalized paths that are "full" paths in said archive, but not on the system

  • Will help in building and working with relative urls.

Proposed Api

namespace System.IO
{
    public class Path
    {
        public static string RemoveRelativeSegments(string path);
        public static string RemoveRelativeSegments(ReadOnlySpan<char> path);
        public static bool TryRemoveRelativeSegments(ReadOnlySpan<char> path, span<char> buffer, out int charsWritten);
    }
}

The rootlength is the length of the path which will never be trimmed while removing the relativeSegments.

Behavior

  • Path.DirectorySeparatorChar and Path.AltDirectorySeparatorChar are considered path "segment" separators (\ and / on Windows, / on Unix)
  • Sequential separators will be collapsed to a single separator
  • Any Path.AltDirectorySeparatorChar characters will be changed to Path.DirectorySeparatorChar (only relevant on Windows).
  • Segments of one period only . will be removed.
  • Segments of two periods only .. will be removed along with the parent segment, if any.

Implementation

This api is already implemented as an internal api and is being used by GetFullPath api to remove relative segments in case of device paths.

internal static bool RemoveRelativeSegments(ReadOnlySpan<char> path, int rootLength, ref ValueStringBuilder sb)

Usage

// removing the relative segment
Assert.Equal("C:\temp", PathInternal.RemoveRelativeSegments("C:\git\..\temp"));

// removing the relative segment but not eating the root
Assert.Equal("C:\temp", PathInternal.RemoveRelativeSegments("C:\..\..\temp"));

// Input is relative path
Assert.Equal("temp", PathInternal.RemoveRelativeSegments("git\..\temp"));

// Multiple types of relative Segments together
Assert.Equal("temp", PathInternal.RemoveRelativeSegments("git\.\..\temp"));
Assert.Equal("git\temp", PathInternal.RemoveRelativeSegments("git\\\\temp"));

// Normalizing the path
Assert.Equal("git\temp\src\corefx", PathInternal.RemoveRelativeSegments("git\temp/src\corefx"));

More Usages can be find here
dotnet/corefx#37225
I have modified the internal tests to api proposal

Some Design Answers

Resolving this https://github.com/dotnet/corefx/issues/4208

Can directly call RemoveRelativeSegments(RelativePath);

Exception Handling

  • return empty string if the path is empty or null.
  • throw Argument Exception if the bufferLength is less than the required in TryRemoveRelativeSegments api

cc @danmosemsft @JeremyKuhne

related implementation prs dotnet/coreclr#24273 dotnet/corefx#37225

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.IOhelp wanted[up-for-grabs] Good issue for external contributors

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions