A comprehensive, cross-platform path manipulation library for Dart.
The pathos library provides common operations for manipulating file paths: joining, splitting, normalizing, etc.
We‘ve tried very hard to make this library do the “right” thing on whatever platform you run it on. When you use the top-level functions, it will assume the host OS’s path style and work with that. If you want to specifically work with paths of a specific style, you can construct a path.Builder
for that style.
The path library was designed to be imported with a prefix, though you don‘t have to if you don’t want to:
import 'package:pathos/path.dart' as path;
The most common way to use the library is through the top-level functions. These manipulate path strings based on your current working directory and the path style (POSIX or Windows) of the host operating system.
Gets the path to the current working directory.
Gets the path separator for the current platform. On Mac and Linux, this is /
. On Windows, it's \
.
Converts [path] to an absolute path by resolving it relative to the current working directory. If [path] is already an absolute path, just returns it.
path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt
Gets the part of [path] after the last separator.
path.basename('path/to/foo.dart'); // -> 'foo.dart' path.basename('path/to'); // -> 'to'
Trailing separators are ignored.
builder.basename('path/to/'); // -> 'to'
Gets the part of [path] after the last separator, and without any trailing file extension.
path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
Trailing separators are ignored.
builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
Gets the part of [path] before the last separator.
path.dirname('path/to/foo.dart'); // -> 'path/to' path.dirname('path/to'); // -> 'to'
Trailing separators are ignored.
builder.dirname('path/to/'); // -> 'path'
Gets the file extension of [path]: the portion of [basename] from the last .
to the end (including the .
itself).
path.extension('path/to/foo.dart'); // -> '.dart' path.extension('path/to/foo'); // -> '' path.extension('path.to/foo'); // -> '' path.extension('path/to/foo.dart.js'); // -> '.js'
If the file name starts with a .
, then that is not considered the extension:
path.extension('~/.bashrc'); // -> '' path.extension('~/.notes.txt'); // -> '.txt'
Returns the root of [path], if it‘s absolute, or the empty string if it’s relative.
// Unix path.rootPrefix('path/to/foo'); // -> '' path.rootPrefix('/path/to/foo'); // -> '/' // Windows path.rootPrefix(r'path\to\foo'); // -> '' path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
Returns true
if [path] is an absolute path and false
if it is a relative path. On POSIX systems, absolute paths start with a /
(forward slash). On Windows, an absolute path starts with \\
, or a drive letter followed by :/
or :\
.
Returns true
if [path] is a relative path and false
if it is absolute. On POSIX systems, absolute paths start with a /
(forward slash). On Windows, an absolute path starts with \\
, or a drive letter followed by :/
or :\
.
Joins the given path parts into a single path using the current platform's [separator]. Example:
path.join('path', 'to', 'foo'); // -> 'path/to/foo'
If any part ends in a path separator, then a redundant separator will not be added:
path.join('path/', 'to', 'foo'); // -> 'path/to/foo
If a part is an absolute path, then anything before that will be ignored:
path.join('path', '/to', 'foo'); // -> '/to/foo'
Splits [path] into its components using the current platform's [separator].
path.split('path/to/foo'); // -> ['path', 'to', 'foo']
The path will not be normalized before splitting.
path.split('path/../foo'); // -> ['path', '..', 'foo']
If [path] is absolute, the root directory will be the first element in the array. Example:
// Unix path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] // Windows path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
Normalizes [path], simplifying it by handling ..
, and .
, and removing redundant path separators whenever possible.
path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
String normalize(String path) => _builder.normalize(path);
Attempts to convert [path] to an equivalent relative path from the current directory.
// Given current directory is /root/path: path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' path.relative('/root/other.dart'); // -> '../other.dart'
If the [from] argument is passed, [path] is made relative to that instead.
path.relative('/root/path/a/b.dart', from: '/root/path'); // -> 'a/b.dart' path.relative('/root/other.dart', from: '/root/path'); // -> '../other.dart'
Since there is no relative path from one drive letter to another on Windows, this will return an absolute path in that case.
path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other'
Removes a trailing extension from the last part of [path].
withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
In addition to the functions, path exposes a path.Builder
class. This lets you configure the root directory and path style that paths are built using explicitly instead of assuming the current working directory and host OS's path style.
You won't often use this, but it can be useful if you do a lot of path manipulation relative to some root directory.
var builder = new path.Builder(root: '/other/root'); builder.relative('/other/root/foo.txt'); // -> 'foo.txt'
It exposes the same methods and getters as the top-level functions, with the addition of:
Creates a new path builder for the given style and root directory.
If [style] is omitted, it uses the host operating system's path style. If [root] is omitted, it defaults to the current working directory. If [root] is relative, it is considered relative to the current working directory.
The style of path that this builder works with.
The root directory that relative paths will be relative to.
Gets the path separator for the builder‘s [style]. On Mac and Linux, this is /
. On Windows, it’s \
.
Returns the root of [path], if it‘s absolute, or an empty string if it’s relative.
// Unix builder.rootPrefix('path/to/foo'); // -> '' builder.rootPrefix('/path/to/foo'); // -> '/' // Windows builder.rootPrefix(r'path\to\foo'); // -> '' builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
Creates a new path by appending the given path parts to the [root]. Equivalent to [join()] with [root] as the first argument. Example:
var builder = new Builder(root: 'root'); builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo'
The path library can work with two different “flavors” of path: POSIX and Windows. The differences between these are encapsulated by the path.Style
enum class. There are two instances of it:
POSIX-style paths use “/” (forward slash) as separators. Absolute paths start with “/”. Used by UNIX, Linux, Mac OS X, and others.
Windows paths use “" (backslash) as separators. Absolute paths start with a drive letter followed by a colon (example, “C:”) or two backslashes (”\") for UNC paths.
Currently, Dart has no way of encapsulating configuration-specific code. Ideally, this library would be able to import dart:io when that's available or dart:html when that is. That would let it seamlessly work on both.
Until then, this only works on the standalone VM. It's API is not coupled to dart:io, but it uses it internally to determine the current working directory.
When you have path objects, then every API that takes a path has to decide if it accepts strings, path objects, or both.
Accepting strings is the most convenient, but then it seems weird to have these path objects that aren‘t actually accepted by anything that needs a path. Once you’ve created a path, you have to always call .toString()
on it before you can do anything useful with it.
Requiring objects forces users to wrap path strings in these objects, which is tedious. It also means coupling that API to whatever library defines this path class. If there are multiple “path” libraries that each define their own path types, then any library that works with paths has to pick which one it uses.
Taking both means you can‘t type your API. That defeats the purpose of having a path type: why have a type if your APIs can’t annotate that they use it?
Given that, we've decided this library should simply treat paths as strings.
We believe this library handles most of the corner cases of Windows paths (POSIX paths are generally pretty straightforward):
It understands that both “/” and "" are valid path separators, not just "".
It can accurately tell if a path is absolute based on drive-letters or UNC prefix.
It understands that “/foo” is not an absolute path on Windows.
It knows that “C:\foo\one.txt” and “c:/foo\two.txt” are two files in the same directory.
If you find a problem, surprise or something that‘s unclear, please don’t hesitate to file a bug and let us know.