Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Operations with the file system

This page describes the operations that request information about the file system or do actions to it.

FilePath

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.

source:/trunk/doc/images/classdia.tango.io.FilePath.png

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.

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:

path.create;

Folders should be created using:

path.createFolder;

Renaming a file can also move it from one place to another:

path.rename ("/directory/otherfilename");

Copying a file retains the original timestamps:

path.copy ("source.name");

Removal of a file or a folder:

path.remove;

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 (void delegate (char[] parent, char[] name, bool isDir) dg);

This version of toList() can be used to construct custom file visitors, and is used for that purpose by the #!FileScan module.

FilePath Exceptions

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.

FileConduit

The principal means of file access is via a FileConduit, providing both streaming and random-access to file content. Opening a file for reading is performed as follows:

auto conduit = new FileConduit ("myFilePath");

Whereas opening a file for writing requires one of the file styles to be specified:

auto conduit = new FileConduit ("myFilePath", FileConduit.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.

FileConduit 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 FileConduit ("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 FileConduit ("test.txt");

// open another for writing
auto to = new FileConduit ("copy.txt", FileConduit.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 FileConduit ("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 FileConduit, like so:

// open file for writing 
auto to = new FileConduit ("text.txt", FileConduit.WriteCreate);

// write an array of content to it 
auto bytesWritten = to.output.write (content);

FileConduit supports random IO also. In this example we relocate the current file position using seek() and, to add a little spice, utilize a DataStream (reader & writer pair) to perform simple typed input and output:

// open a file for reading and writing
auto file = new FileConduit ("random.bin", FileConduit.ReadWriteCreate);

// bind a DataStream 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 DataStream IO is buffered 
output.putInt (x);
output.put    (y);
output.flush ();

// rewind to file start
file.seek (0);

// read data back again
x = input.getInt;
input.get (y);

Note that in the above example we are using buffered IO, via the DataStream, and thus need to flush the output before relocating the current position.

Each FileConduit should be explicitly closed when no longer needed. It can often be convenient to use a scope expression for this purpose:

auto file = new FileConduit ("myFilePath");
scope (exit)
       file.close;

source:/trunk/doc/images/classdia.tango.io.FileConduit.png

FileConduit Exceptions

IO exceptions are raised where a bulk read or write operation fails entirely, or where a copy operation fails to complete. This might happen if, for example, a remote file were to suddenly become unavailable whilst in use.

File

File is a simple class for read, write or append file content at once.

source:/trunk/doc/images/classdia.tango.io.File.png

For example, to read all file content:

auto file = new File ("myfile");
auto content = file.read();

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 file = new File ("myfile");
auto content = cast(char[]) file.read();

To convert a text file into a set of lines, try the following:

import Text = tango.text.Util;

auto file = new File ("myfile");
auto content = cast(char[]) file.read;
auto lines = Text.splitLines (content);

Using a foreach to iterate instead:

foreach (line; Text.lines (content))
         // do something with each line

Files can be set to the content of an array:

char[] myText;

file.write (myText);

File content may be appended in a similar fashion:

char[] myText;

file.append (myText);

Each of the methods belonging to FilePath are exposed via the path method, so you can get the file size, relocate it, remove it, and so on.

File Exceptions

File will throw IO exceptions where an underlying OS or file-system error occurs. This might happen when, for example, an attempt is made to write a read-only file.

FileSystem

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.

FileSystem Exceptions

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.

FileRoots

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

FileRoots Exceptions

IO exceptions will be throws where an underlying OS or file-system error occurs.

FileScan

The FileScan module wraps functionality from FilePath.toList to provide something more concrete. The principal distinction is that FileScan recurses folder-trees, and generates a list of both files and the folders containing them.

To generate a list of D files, and the folders where they reside, try this:

import tango.io.FileScan;

auto scan = new FileScan;
scan (new FilePath("."), ".d");

foreach (folder; scan.folders)
         Cout (folder).newline;

foreach (file; scan.files)
         Cout (file).newline;

This example executes a sweep across all files ending with ".d", beginning at the current directory, and extending across all sub-directories. Each folder containing a located file is displayed on the console, followed by a list of the located file names.

FileScan has a number of ways to override the simplistic file filtering illustrated above, using a delegate:

bool delegate (FilePath path, bool isDir)

The return value of the delegate should be true to add the instance, or false to ignore it. Argument isDir indicated whether the or not the instance is a folder or a file.

FileScan Exceptions

No explicit exceptions are thrown, but those from FilePath.toList will be exposed to the caller – generally file-system failures reported by the underlying OS.