This module is used to manipulate path strings.
All functions, with the exception of expandTilde
(and in some
cases absolutePath
and relativePath
), are pure
string manipulation functions; they don't depend on any state outside
the program, nor do they perform any actual file system actions.
This has the consequence that the module does not make any distinction
between a path that points to a directory and a path that points to a
file, and it does not know whether or not the object pointed to by the
path actually exists in the file system.
To differentiate between these cases, use std.file.isDir and
std.file.exists.
Note that on Windows, both the backslash (`\`) and the slash (`/`)
are in principle valid directory separators. This module treats them
both on equal footing, but in cases where a new separator is
added, a backslash will be used. Furthermore, the buildNormalizedPath
function will replace all slashes with backslashes on that platform.
In general, the functions in this module assume that the input paths
are well-formed. (That is, they should not contain invalid characters,
they should follow the file system's path format, etc.) The result
of calling a function on an ill-formed path is undefined. When there
is a chance that a path or a file name is invalid (for instance, when it
has been input by the user), it may sometimes be desirable to use the
isValidFilename
and isValidPath
functions to check
this.
Most functions do not perform any memory allocations, and if a string is
returned, it is usually a slice of an input string. If a function
allocates, this is explicitly mentioned in the documentation.
String used to separate directory names in a path. Under POSIX this is a slash, under Windows a backslash.
Path separator string. A colon under POSIX, a semicolon under Windows.
Determines whether the given character is a directory separator.
On Windows, this includes both `\` and `/`. On POSIX, it's just `/`.
This enum is used as a template argument to functions which compare file names, and determines whether the comparison is case sensitive or not.
Returns the name of a file, without any leading directory and with an optional suffix chopped off.
If suffix is specified, it will be compared to path using filenameCmp!cs, where cs is an optional template parameter determining whether the comparison is case sensitive or not. See the filenameCmp documentation for details.
assert (baseName("dir/file.ext") == "file.ext"); assert (baseName("dir/file.ext", ".ext") == "file"); assert (baseName("dir/file.ext", ".xyz") == "file.ext"); assert (baseName("dir/filename", "name") == "file"); assert (baseName("dir/subdir/") == "subdir"); version (Windows) { assert (baseName(`d:file.ext`) == "file.ext"); assert (baseName(`d:\dir\file.ext`) == "file.ext"); }
assert (baseName(stripExtension("dir/file.ext")) == "file");
Returns the directory part of a path. On Windows, this includes the drive letter if present.
This function performs a memory allocation if and only if path is mutable and does not have a directory (in which case a new mutable string is needed to hold the returned current-directory symbol, ".").
assert (dirName("file") == "."); assert (dirName("dir/file") == "dir"); assert (dirName("/file") == "/"); assert (dirName("dir/subdir/") == "dir"); version (Windows) { assert (dirName("d:file") == "d:"); assert (dirName(`d:\dir\file`) == `d:\dir`); assert (dirName(`d:\file`) == `d:\`); assert (dirName(`dir\subdir\`) == `dir`); }
Returns the root directory of the specified path, or null if the path is not rooted.
assert (rootName("foo") is null); assert (rootName("/foo") == "/"); version (Windows) { assert (rootName(`\foo`) == `\`); assert (rootName(`c:\foo`) == `c:\`); assert (rootName(`\\server\share\foo`) == `\\server\share`); }
Returns the drive of a path, or null if the drive is not specified. In the case of UNC paths, the network share is returned.
Always returns null on POSIX.
version (Windows) { assert (driveName(`d:\file`) == "d:"); assert (driveName(`\\server\share\file`) == `\\server\share`); assert (driveName(`dir\file`).empty); }
Strips the drive from a Windows path. On POSIX, the path is returned unaltered.
version (Windows) { assert (stripDrive(`d:\dir\file`) == `\dir\file`); assert (stripDrive(`\\server\share\dir\file`) == `\dir\file`); }
Returns the extension part of a file name, including the dot.
If there is no extension, null is returned.
assert (extension("file").empty); assert (extension("file.ext") == ".ext"); assert (extension("file.ext1.ext2") == ".ext2"); assert (extension("file.") == "."); assert (extension(".file").empty); assert (extension(".file.ext") == ".ext");
Returns the path with the extension stripped off.
assert (stripExtension("file") == "file"); assert (stripExtension("file.ext") == "file"); assert (stripExtension("file.ext1.ext2") == "file.ext1"); assert (stripExtension("file.") == "file"); assert (stripExtension(".file") == ".file"); assert (stripExtension(".file.ext") == ".file"); assert (stripExtension("dir/file.ext") == "dir/file");
Returns a string containing the path given by path, but where the extension has been set to ext.
If the filename already has an extension, it is replaced. If not, the
extension is simply appended to the filename. Including a leading dot
in ext is optional.
If the extension is empty, this function is equivalent to
stripExtension
.
This function normally allocates a new string (the possible exception
being the case when path is immutable and doesn't already have an
extension).
assert (setExtension("file", "ext") == "file.ext"); assert (setExtension("file", ".ext") == "file.ext"); assert (setExtension("file.old", "") == "file"); assert (setExtension("file.old", "new") == "file.new"); assert (setExtension("file.old", ".new") == "file.new");
Returns the path given by path, with the extension given by ext appended if the path doesn't already have one.
Including the dot in the extension is optional.
This function always allocates a new string, except in the case when
path is immutable and already has an extension.
assert (defaultExtension("file", "ext") == "file.ext"); assert (defaultExtension("file", ".ext") == "file.ext"); assert (defaultExtension("file.", "ext") == "file."); assert (defaultExtension("file.old", "new") == "file.old"); assert (defaultExtension("file.old", ".new") == "file.old");
Combines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute
), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. `\foo`), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
version (Posix) { assert (buildPath("foo", "bar", "baz") == "foo/bar/baz"); assert (buildPath("/foo/", "bar/baz") == "/foo/bar/baz"); assert (buildPath("/foo", "/bar") == "/bar"); } version (Windows) { assert (buildPath("foo", "bar", "baz") == `foo\bar\baz`); assert (buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`); assert (buildPath("foo", `d:\bar`) == `d:\bar`); assert (buildPath("foo", `\bar`) == `\bar`); assert (buildPath(`c:\foo`, `\bar`) == `c:\bar`); }
Performs the same task as buildPath , while at the same time resolving current/parent directory symbols ("." and "..") and removing superfluous directory separators. On Windows, slashes are replaced with backslashes.
Note that this function does not resolve symbolic links.
This function always allocates memory to hold the resulting path.
version (Posix) { assert (buildNormalizedPath("/foo/./bar/..//baz/") == "/foo/baz"); assert (buildNormalizedPath("../foo/.") == "../foo"); assert (buildNormalizedPath("/foo", "bar/baz/") == "/foo/bar/baz"); assert (buildNormalizedPath("/foo", "/bar/..", "baz") == "/baz"); assert (buildNormalizedPath("foo/./bar", "../../", "../baz") == "../baz"); assert (buildNormalizedPath("/foo/./bar", "../../baz") == "/baz"); } version (Windows) { assert (buildNormalizedPath(`c:\foo\.\bar/..\\baz\`) == `c:\foo\baz`); assert (buildNormalizedPath(`..\foo\.`) == `..\foo`); assert (buildNormalizedPath(`c:\foo`, `bar\baz\`) == `c:\foo\bar\baz`); assert (buildNormalizedPath(`c:\foo`, `bar/..`) == `c:\foo`); assert (buildNormalizedPath(`\\server\share\foo`, `..\bar`) == `\\server\share\bar`); }
Returns a bidirectional range that iterates over the elements of a path.
assert (equal(pathSplitter("/"), ["/"])); assert (equal(pathSplitter("/foo/bar"), ["/", "foo", "bar"])); assert (equal(pathSplitter("//foo/bar"), ["//foo", "bar"])); assert (equal(pathSplitter("foo/../bar//./"), ["foo", "..", "bar", "."])); version (Windows) { assert (equal(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."])); assert (equal(pathSplitter("c:"), ["c:"])); assert (equal(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"])); assert (equal(pathSplitter(`c:foo\bar`), ["c:foo", "bar"])); }
Determines whether a path starts at a root directory.
On POSIX, this function returns true if and only if the path starts with a slash (/).
version (Posix) { assert (isRooted("/")); assert (isRooted("/foo")); assert (!isRooted("foo")); assert (!isRooted("../foo")); }
version (Windows) { assert (isRooted(`\`)); assert (isRooted(`\foo`)); assert (isRooted(`d:\foo`)); assert (isRooted(`\\foo\bar`)); assert (!isRooted("foo")); assert (!isRooted("d:foo")); }
Determines whether a path is absolute or not.
version (Posix) { assert (isAbsolute("/")); assert (isAbsolute("/foo")); assert (!isAbsolute("foo")); assert (!isAbsolute("../foo")); }
version (Windows) { assert (isAbsolute(`d:\`)); assert (isAbsolute(`d:\foo`)); assert (isAbsolute(`\\foo\bar`)); assert (!isAbsolute(`\`)); assert (!isAbsolute(`\foo`)); assert (!isAbsolute("d:foo")); }
Translates path into an absolute path.
The following algorithm is used:
version (Posix) { assert (absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file"); assert (absolutePath("../file", "/foo/bar") == "/foo/bar/../file"); assert (absolutePath("/some/file", "/foo/bar") == "/some/file"); } version (Windows) { assert (absolutePath(`some\file`, `c:\foo\bar`) == `c:\foo\bar\some\file`); assert (absolutePath(`..\file`, `c:\foo\bar`) == `c:\foo\bar\..\file`); assert (absolutePath(`c:\some\file`, `c:\foo\bar`) == `c:\some\file`); assert (absolutePath(`\file`, `c:\foo\bar`) == `c:\file`); }
Translates path into a relative path.
The returned path is relative to base, which is by default
taken to be the current working directory. If specified,
base must be an absolute path, and it is always assumed
to refer to a directory. If path and base refer to
the same directory, the function returns `.`.
The following algorithm is used:
assert (relativePath("foo") == "foo"); version (Posix) { assert (relativePath("foo", "/bar") == "foo"); assert (relativePath("/foo/bar", "/foo/bar") == "."); assert (relativePath("/foo/bar", "/foo/baz") == "../bar"); assert (relativePath("/foo/bar/baz", "/foo/woo/wee") == "../../bar/baz"); assert (relativePath("/foo/bar/baz", "/foo/bar") == "baz"); } version (Windows) { assert (relativePath("foo", `c:\bar`) == "foo"); assert (relativePath(`c:\foo\bar`, `c:\foo\bar`) == "."); assert (relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`); assert (relativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`); assert (relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz"); assert (relativePath(`c:\foo\bar`, `d:\foo`) == `c:\foo\bar`); }
Compares filename characters and return < 0 if a < b, 0 if a == b and > 0 if a > b.
This function can perform a case-sensitive or a case-insensitive
comparison. This is controlled through the cs template parameter
which, if not specified, is given by
CaseSensitive
.osDefault.
On Windows, the backslash and slash characters (`\` and `/`)
are considered equal.
assert (filenameCharCmp('a', 'a') == 0); assert (filenameCharCmp('a', 'b') < 0); assert (filenameCharCmp('b', 'a') > 0); version (linux) { // Same as calling filenameCharCmp!(CaseSensitive.yes)(a, b) assert (filenameCharCmp('A', 'a') < 0); assert (filenameCharCmp('a', 'A') > 0); } version (Windows) { // Same as calling filenameCharCmp!(CaseSensitive.no)(a, b) assert (filenameCharCmp('a', 'A') == 0); assert (filenameCharCmp('a', 'B') < 0); assert (filenameCharCmp('A', 'b') < 0); }
Compares file names and returns < 0 if filename1 < filename2, 0 if filename1 == filename2 and > 0 if filename1 > filename2.
Individual characters are compared using filenameCharCmp!cs, where cs is an optional template parameter determining whether the comparison is case sensitive or not. See the filenameCharCmp documentation for details.
assert (filenameCmp("abc", "abc") == 0); assert (filenameCmp("abc", "abd") < 0); assert (filenameCmp("abc", "abb") > 0); assert (filenameCmp("abc", "abcd") < 0); assert (filenameCmp("abcd", "abc") > 0); version (linux) { // Same as calling filenameCmp!(CaseSensitive.yes)(filename1, filename2) assert (filenameCmp("Abc", "abc") < 0); assert (filenameCmp("abc", "Abc") > 0); } version (Windows) { // Same as calling filenameCmp!(CaseSensitive.no)(filename1, filename2) assert (filenameCmp("Abc", "abc") == 0); assert (filenameCmp("abc", "Abc") == 0); assert (filenameCmp("Abc", "abD") < 0); assert (filenameCmp("abc", "AbB") > 0); }
Matches a pattern against a path.
Some characters of pattern have a special meaning (they are
meta-characters) and can't be escaped. These are:
* | Matches 0 or more instances of any character. |
? | Matches exactly one instance of any character. |
[chars] | Matches one instance of any character that appears between the brackets. |
[!chars] | Matches one instance of any character that does not appear between the brackets after the exclamation mark. |
{string1,string2,…} | Matches either of the specified strings. |
assert (globMatch("foo.bar", "*")); assert (globMatch("foo.bar", "*.*")); assert (globMatch(`foo/foo\bar`, "f*b*r")); assert (globMatch("foo.bar", "f???bar")); assert (globMatch("foo.bar", "[fg]???bar")); assert (globMatch("foo.bar", "[!gh]*bar")); assert (globMatch("bar.fooz", "bar.{foo,bif}z")); assert (globMatch("bar.bifz", "bar.{foo,bif}z")); version (Windows) { // Same as calling globMatch!(CaseSensitive.no)(path, pattern) assert (globMatch("foo", "Foo")); assert (globMatch("Goo.bar", "[fg]???bar")); } version (linux) { // Same as calling globMatch!(CaseSensitive.yes)(path, pattern) assert (!globMatch("foo", "Foo")); assert (!globMatch("Goo.bar", "[fg]???bar")); }
Checks that the given file or directory name is valid.
This function returns true if and only if filename is not
empty, not too long, and does not contain invalid characters.
The maximum length of filename is given by the constant
core.stdc.stdio.FILENAME_MAX. (On Windows, this number is
defined as the maximum number of UTF-16 code points, and the
test will therefore only yield strictly correct results when
filename is a string of wchars.)
On Windows, the following criteria must be satisfied
(source):
Checks whether path is a valid path.
Generally, this function checks that path is not empty, and that
each component of the path either satisfies isValidFilename
or is equal to "." or "..".
It does not check whether the path points to an existing file
or directory; use std.file.exists for this purpose.
On Windows, some special rules apply:
Performs tilde expansion in paths on POSIX systems. On Windows, this function does nothing.
There are two ways of using tilde expansion in a path. One
involves using the tilde alone or followed by a path separator. In
this case, the tilde will be expanded with the value of the
environment variable HOME. The second way is putting
a username after the tilde (i.e. ~john/Mail). Here,
the username will be searched for in the user database
(i.e. /etc/passwd on Unix systems) and will expand to
whatever path is stored there. The username is considered the
string after the tilde ending at the first instance of a path
separator.
Note that using the ~user syntax may give different
values from just ~ if the environment variable doesn't
match the value stored in the user database.
When the environment variable version is used, the path won't
be modified if the environment variable doesn't exist or it
is empty. When the database version is used, the path won't be
modified if the user doesn't exist in the database or there is
not enough memory to perform the query.
This function performs several memory allocations.
void processFile(string path) { // Allow calling this function with paths such as ~/foo auto fullPath = expandTilde(path); ... }