Operations with the file system
This page describes the operations that request information about the file system or do actions to it.
In the Tango library, file and directory locations are usually described by a FilePath instance. Those methods accepting a char instead will often wrap it with a FilePath before continuing.
FilePath itself exposes the constituent components of the file-path as distinct elements, and performs a variety of transformations upon the original path. Methods are available to return the path portion, the file name, the file extension, file suffix (which may have multiple dots) and so on. Transformations support the replacement of one or more components, and return a complete file-path as a char rather than another FilePath instance. The latter is specifically to support the use of stack-based class instances.
FilePath assumes both path and name are present within the provided file-path, and therefore may split what is otherwise a logically valid path. That is, the name attribute of a FilePath is considered to be the segment following a rightmost path-separator; therefore a directory identifier may be mapped to the name instead of explicitly remaining with the path attribute. This follows the intent of treating files and directories in an identical manner – as a name with an optional ancestral structure. However, where this is not desired it is possible (and legitimate) to bias the interpretation by adding a trailing path-separator. Doing so will result in an empty name attribute and a longer path attribute.
When a FilePath is created, it usually makes a copy of the provided file-path argument. Prior experience with a non-copying implementation had shown the common case to be an explicit .dup on the argument, whereas aliasing appeared to be rare by comparison. It was also noted that a large proportion were used for interaction with C-oriented OS calls, implying the postfix of a null terminator. FilePath combines both operations as part of the generic constructor, whilst leaving an option open to alias where appropriate.
FilePath is intended to be mutable, thus each transformation modifies the internal content. There is a read-only view of FilePath (PathView) that can be used to expose an immutable perspective. The current class-based implementation will likely migrate to a struct version once the D language gains support for struct-constructors (see module tango.io.Path for more information).
A number of common file and directory operations are exposed via FilePath, including creation, renaming, removal, and folder/directory content listing. A handful of attributes such as file size and various timestamps are also made available.
Creating a FilePath is straightforward; provide the constructor with a char. File paths containing non-ANSI characters should be UTF-8 encoded:
auto path = new FilePath ("name");
Creating files and folders require distinction between the two. For files, use:
Folders should be created using:
Renaming a file can also move it from one place to another:
Copying a file retains the original timestamps:
Removal of a file or a folder:
Listing the content of a folder:
foreach (name; path.toList) // do something with the contained file or folder name
There’s an additional, lower-level toList() which exposes further control via a delegate:
path.toList (bool delegate (FilePath path, bool isFolder) dg);
This version of toList() can be used to construct custom file visitors.
IO exceptions are raised where the underlying OS detects an error. For example, attempting to remove a non-existent or read-only file will generate an exception.
The principal means of file access is via a File, providing both streaming and random-access to file content. Opening a file for reading is performed as follows:
auto conduit = new File ("myFilePath");
Whereas opening a file for writing requires one of the file styles to be specified:
auto conduit = new File ("myFilePath", File.WriteCreate);
There are a variety of pre-defined styles, including appending, read-only, read-write, create-always and so on. Additional styles can be defined, using a combination of a dozen system-level flags.
File enables direct, type-agnostic access to file content. In this example we copy a file directly to the console:
// open a file for reading auto from = new File ("test.txt"); // display file content on console Stdout.stream.copy (from);
And here we copy one file to another:
// open a file for reading auto from = new File ("test.txt"); // open another for writing auto to = new File ("copy.txt", File.WriteCreate); // copy file to.output.copy (from);
To load an entire file into memory, one might consider the following:
// open file for reading auto file = new File ("test.txt"); // create an array to house the entire file auto content = new char[file.length]; // read the file content. Return value is the number of bytes read auto bytesRead = file.input.read (content);
Conversely, one may write directly to a File, like so:
// open file for writing auto to = new File ("text.txt", File.WriteCreate); // write an array of content to it auto bytesWritten = to.output.write (content);
File supports random IO also. In this example we relocate the current file position using seek() and, to add a little spice, utilize a Data stream (reader & writer pair) to perform simple typed input and output:
// open a file for reading and writing auto file = new File ("random.bin", File.ReadWriteCreate); // bind a Data stream to this conduit auto input = new DataInput (file); auto output = new DataOutput (file); int x = 10; char y = "hello"; // write data, and flush output since Data IO is buffered output.int32 (x); output.array (y); output.flush; // rewind to file start file.seek (0); // read data back again x = input.int32; y = input.array;
Note that in the above example we are using buffered IO, via the Data stream, and thus need to flush the output before relocating the current position.
Each File should be explicitly closed when no longer needed. It can often be convenient to use a scope expression for this purpose:
scope file = new File ("myFilePath");
As a convenience, File has static functions to read, write or append. For example, to read all file content:
auto content = File.get ("myfile");
The underlying file is closed before the call returns. File must avoid making assumptions about the file content, so the above example returns an array of void. When working with text files, it is necessary to cast the return value to represent the correct data type. For text files this is often a char:
auto content = cast(char) File.get ("myfile");
To convert a text file into a set of lines, try the following:
import Text = tango.text.Util; auto content = cast(char) File.get ("myfile"); auto lines = Text.splitLines (content);
Using a foreach to iterate instead:
foreach (line; Text.lines (cast(char) File.get("myfile")) // do something with each line
Files can be set to the content of an array:
char myText; File.set ("myfile", myText);
File content may be appended in a similar fashion:
char myText; File.append ("myfile", myText);
IO exceptions are raised where a bulk read or write operation fails entirely, or where a copy operation fails to complete. This might happen when, for example, attempting to write a read-only file.
This is where various file-system controls are exposed. At this time, FileSystem provides facilities for retrieving and setting the current working directory, and for converting a path into its absolute form. To access the current directory name:
auto name = FileSystem.getDirectory;
Changing the current directory is similar.
Method absolutePath accepts a FilePath instance, and converts it into absolute form relevant to the current working directory. Absolute form generally begins with a path separator, or a storage device identifier, and contains no instances of '.' or '..' anywhere in the path. Where the provided path is already absolute, it is returned untouched.
Failing to set or retrieve the current directory will cause an exception to be thrown. Passing an invalid path to absolutePath will also result in an exception being thrown.
The storage-devices of the file-system are exposed here. On Win32, roots represent drive letters, whilst on linux they represent devices located via /etc/mtab/. To list the file storage devices available:
foreach (name; FileRoots.list) // do something with each name
IO exceptions will be throws where an underlying OS or file-system error occurs.