Path Formats
Overview
Every path carries a PathFormat describing the rules it follows: separator character, what counts as a valid entry name, whether absolute or rooted-relative paths are allowed, and so on. The library exposes three format singletons plus a Current accessor.
Choosing the right format up front lets you parse paths from any platform, store paths in a portable way, and convert between formats explicitly.
The Three Formats
PathFormat.Windows
Native Windows path handling. Backslash separator (forward slashes are normalized to backslashes during parsing). Supports drive-letter roots (C:\...), UNC roots (\\server\share\...) and rooted-relative paths (\Some\Path).
PathFormat.Unix
Native Unix path handling. Forward-slash separator. Supports absolute paths (/var/data) and non-rooted relative paths (some/path). Rooted-relative paths are not supported; anything starting with / is absolute.
PathFormat.Universal
A cross-platform format that only allows constructs valid on every platform:
- Forward-slash separator only (backslashes are rejected, since they're valid filename characters on Unix).
- Only relative, non-rooted paths. Absolute paths and rooted-relative paths are platform concepts and don't belong in the universal format.
- Strict entry name validation that rejects anything Windows would object to.
Tip
Use PathFormat.Universal when storing relative paths in databases, configuration files or any data that may be read from another platform. The format guarantees the stored string will parse and behave identically everywhere.
PathFormat.Current
Returns PathFormat.Windows on Windows and PathFormat.Unix on Unix-based platforms (Linux, macOS, etc.). This is the default for every parse method when no format is specified.
Format Restrictions on I/O
File system operations (OpenStream, Create, Delete, enumeration, etc.) only work on absolute paths whose PathFormat matches PathFormat.Current. Trying to perform I/O on a non-current path throws an InvalidOperationException (or ArgumentException when the path is a method argument).
var winPath = FilePath.ParseAbsolute(@"C:\data\file.txt", PathFormat.Windows);
winPath.OpenStream(); // works on Windows; throws on Unix
Important
Parsing a path with a non-current format is purely for manipulation, conversion or storage. To actually access the file system, the path must be in the current platform's format.
Format Conversion
Relative paths can be converted between formats with IRelativePath.ToPathFormat:
IRelativeFilePath universal = FilePath.ParseRelative("data/users.json", PathFormat.Universal);
IRelativeFilePath windows = universal.ToPathFormat(PathFormat.Windows); // "data\users.json"
IRelativeFilePath unix = universal.ToPathFormat(PathFormat.Unix); // "data/users.json"
Conversion may throw ArgumentException if the path can't be represented in the target format. For example, a Unix path containing characters that are valid as filename characters on Unix but reserved on Windows:
var unixPath = FilePath.ParseRelative("some/file?.txt", PathFormat.Unix, PathOptions.None);
winPath.ToPathFormat(PathFormat.Windows); // throws: '?' is not valid in Windows
Note
Absolute paths are platform-specific by definition and cannot be format-converted. To "move" an absolute path to another platform's format, take its relative remainder (relative to a known root) and convert that.
Combining Across Formats
When combining a directory with a relative path, the formats are reconciled as follows:
| Directory format | Relative format | Result format |
|---|---|---|
| Windows | Windows | Windows |
| Unix | Unix | Unix |
| Universal | Universal | Universal |
| Windows | Universal | Windows |
| Unix | Universal | Unix |
| Windows | Unix | error |
| Unix | Windows | error |
In short: matching formats win, Universal yields to whichever specific format is on the other side, and mixing two specific formats is an error. This means a PathFormat.Universal relative path is freely combinable with any platform-specific directory.
IRelativeFilePath cfg = FilePath.ParseRelative("config/app.json", PathFormat.Universal);
IAbsoluteDirectoryPath baseDir = DirectoryPath.GetAppBase(); // current format
IAbsoluteFilePath fullPath = baseDir + cfg; // current format
Three String Forms
Every path has three string representations. Use the right one for the job.
PathDisplay
Friendly, human-readable. Suitable for:
- Display in UI, logs and error messages.
- Storage and serialization that you'll re-parse with this library.
PathDisplay round-trips cleanly through the matching parse method.
file.PathDisplay; // "C:\Apps\MyApp\config.json"
PathExport (absolute paths only)
Specially formatted for handing to non-library APIs. On Windows, this typically prefixes with the \\?\ extended-path syntax so the file system never silently mutates the path (no whitespace trimming, no reserved-name remapping).
Use PathExport whenever you need a string for System.IO, native interop, or any third-party API that takes a path string:
using var stream = new FileStream(file.PathExport, FileMode.Open);
Important
Use PathExport, never PathDisplay, when calling APIs outside this library. PathDisplay looks normal but can be silently rewritten by lower-level path handling. PathExport cannot.
ToString()
Returns a deliberately unusable diagnostic string of the form [Format] File: <pathDisplay> or [Format] Directory: <pathDisplay>. Useful in debug output, exceptions and logs, but never pass it to anything that expects a path.
file.ToString(); // "[Windows] File: C:\Apps\MyApp\config.json"
Format-Aware Members
A few PathFormat members are useful from application code:
Separator: the format's path separator character.SupportsRelativeRootedPaths:truefor Windows only.RelativeCurrentDirectory,RelativeParentDirectory: pre-built.and..paths in the format.IsValidExtension(extension, options): validate a file extension against the format's rules.
PathFormat.Windows.Separator; // '\\'
PathFormat.Universal.IsValidExtension(".tar.gz"); // false: multiple dots
PathFormat.Universal.IsValidExtension(".gz"); // true
Next Steps
- PathOptions: interacts with
PathFormat(e.g.PathFormatDependent). - Combining and Navigating Paths: cross-format combine rules in practice.
- Interop and Migration: when to use
PathExport.