Книга: Practical Common Lisp
How Pathnames Represent Filenames
How Pathnames Represent Filenames
A pathname is a structured object that represents a filename using six components: host, device, directory, name, type, and version. Most of these components take on atomic values, usually strings; only the directory component is further structured, containing a list of directory names (as strings) prefaced with the keyword :absolute
or :relative
. However, not all pathname components are needed on all platforms—this is one of the reasons pathnames strike many new Lispers as gratuitously complex. On the other hand, you don't really need to worry about which components may or may not be used to represent names on a particular file system unless you need to create a new pathname object from scratch, which you'll almost never need to do. Instead, you'll usually get hold of pathname objects either by letting the implementation parse a file system-specific namestring into a pathname object or by creating a new pathname that takes most of its components from an existing pathname.
For instance, to translate a namestring to a pathname, you use the PATHNAME
function. It takes a pathname designator and returns an equivalent pathname object. When the designator is already a pathname, it's simply returned. When it's a stream, the original filename is extracted and returned. When the designator is a namestring, however, it's parsed according to the local filename syntax. The language standard, as a platform-neutral document, doesn't specify any particular mapping from namestring to pathname, but most implementations follow the same conventions on a given operating system.
On Unix file systems, only the directory, name, and type components are typically used. On Windows, one more component—usually the device or host—holds the drive letter. On these platforms, a namestring is parsed by first splitting it into elements on the path separator—a slash on Unix and a slash or backslash on Windows. The drive letter on Windows will be placed into either the device or the host component. All but the last of the other name elements are placed in a list starting with :absolute
or :relative
depending on whether the name (ignoring the drive letter, if any) began with a path separator. This list becomes the directory component of the pathname. The last element is then split on the rightmost dot, if any, and the two parts put into the name and type components of the pathname.[157]
You can examine these individual components of a pathname with the functions PATHNAME-DIRECTORY
, PATHNAME-NAME
, and PATHNAME-TYPE
.
(pathname-directory (pathname "/foo/bar/baz.txt")) ==> (:ABSOLUTE "foo" "bar")
(pathname-name (pathname "/foo/bar/baz.txt")) ==> "baz"
(pathname-type (pathname "/foo/bar/baz.txt")) ==> "txt"
Three other functions—PATHNAME-HOST
, PATHNAME-DEVICE
, and PATHNAME-VERSION
—allow you to get at the other three pathname components, though they're unlikely to have interesting values on Unix. On Windows either PATHNAME-HOST
or PATHNAME-DEVICE
will return the drive letter.
Like many other built-in objects, pathnames have their own read syntax, #p
followed by a double-quoted string. This allows you to print and read back s-expressions containing pathname objects, but because the syntax depends on the namestring parsing algorithm, such data isn't necessarily portable between operating systems.
(pathname "/foo/bar/baz.txt") ==> #p"/foo/bar/baz.txt"
To translate a pathname back to a namestring—for instance, to present to the user—you can use the function NAMESTRING
, which takes a pathname designator and returns a namestring. Two other functions, DIRECTORY-NAMESTRING
and FILE-NAMESTRING
, return a partial namestring. DIRECTORY-NAMESTRING
combines the elements of the directory component into a local directory name, and FILE-NAMESTRING
combines the name and type components.[158]
(namestring #p"/foo/bar/baz.txt") ==> "/foo/bar/baz.txt"
(directory-namestring #p"/foo/bar/baz.txt") ==> "/foo/bar/"
(file-namestring #p"/foo/bar/baz.txt") ==> "baz.txt"