dotnet/runtime

Add Path.RemoveRelativeSegments Api

Open

#2,162 opened on Jun 27, 2018

View on GitHub
 (30 comments) (19 reactions) (0 assignees)C# (5,445 forks)batch import
api-approvedarea-System.IOhelp wanted

Repository metrics

Stars
 (17,886 stars)
PR merge metrics
 (Avg merge 12d 11h) (661 merged PRs in 30d)

Description

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. https://github.com/dotnet/runtime/blob/96d9a47228b0412945d151a79af7bf66463b053a/src/libraries/System.Private.CoreLib/src/System/IO/PathInternal.cs#L129

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 https://github.com/dotnet/corefx/pull/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 https://github.com/dotnet/coreclr/pull/24273 https://github.com/dotnet/corefx/pull/37225

Contributor guide