Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro CSharp 2008 And The .NET 3.5 Platform [eng]

.pdf
Скачиваний:
78
Добавлен:
16.08.2013
Размер:
22.5 Mб
Скачать

662 CHAPTER 20 FILE I/O AND ISOLATED STORAGE

Table 20-1. Continued

Nonabstract I/O Class Type

Meaning in Life

DriveInfo

This type provides detailed information regarding the drives used

 

by a given machine.

File, FileInfo

These types are used to manipulate a machine’s set of files. The

 

File type exposes functionality using static members. The FileInfo

 

type exposes similar functionality from a valid object reference.

FileStream

This type allows for random file access (e.g., seeking capabilities)

 

with data represented as a stream of bytes.

FileSystemWatcher

This type allows you to monitor the modification of external files

 

in a specified directory.

MemoryStream

This type provides random access to streamed data stored in

 

memory rather than a physical file.

Path

This type performs operations on System.String types that contain

 

file or directory path information in a platform-neutral manner.

StreamWriter, StreamReader

These types are used to store (and retrieve) textual information to

 

(or from) a file. These types do not support random file access.

StringWriter, StringReader

Like the StreamReader/StreamWriter types, these classes also work

 

with textual information. However, the underlying storage is a

 

string buffer rather than a physical file.

 

 

In addition to these concrete class types, System.IO defines a number of enumerations, as well as a set of abstract classes (Stream, TextReader, TextWriter, and so forth), that define a shared polymorphic interface to all descendents. You will read about many of these types in this chapter.

The Directory(Info) and File(Info) Types

System.IO provides four types that allow you to manipulate individual files, as well as interact with a machine’s directory structure. The first two types, Directory and File, expose creation, deletion, copying, and moving operations using various static members. The closely related FileInfo and DirectoryInfo types expose similar functionality as instance-level methods (and therefore must be allocated with the new keyword). In Figure 20-1, notice that the Directory and File types directly extend

System.Object, while DirectoryInfo and FileInfo derive from the abstract FileSystemInfo type.

Figure 20-1. The File- and Directory-centric types

CHAPTER 20 FILE I/O AND ISOLATED STORAGE

663

Generally speaking, FileInfo and DirectoryInfo are better choices for obtaining full details of a file or directory (e.g., time created, read/write capabilities, etc.), as their members tend to return strongly typed objects. In contrast, the Directory and File class members tend to return simple string values rather than strongly typed objects.

The Abstract FileSystemInfo Base Class

The DirectoryInfo and FileInfo types receive many behaviors from the abstract FileSystemInfo base class. For the most part, the members of the FileSystemInfo class are used to discover general characteristics (such as time of creation, various attributes, and so forth) about a given file or directory. Table 20-2 lists some core properties of interest.

Table 20-2. FileSystemInfo Properties

Property

Meaning in Life

Attributes

Gets or sets the attributes associated with the current file that are represented

 

by the FileAttributes enumeration

CreationTime

Gets or sets the time of creation for the current file or directory

Exists

Can be used to determine whether a given file or directory exists

Extension

Retrieves a file’s extension

FullName

Gets the full path of the directory or file

LastAccessTime

Gets or sets the time the current file or directory was last accessed

LastWriteTime

Gets or sets the time when the current file or directory was last written to

Name

Obtains the name of the current file or directory

 

 

FileSystemInfo also defines the Delete() method. This is implemented by derived types to delete a given file or directory from the hard drive. As well, Refresh() can be called prior to obtaining attribute information to ensure that the statistics regarding the current file (or directory) are not outdated.

Working with the DirectoryInfo Type

The first creatable I/O-centric type you will examine is the DirectoryInfo class. This class contains a set of members used for creating, moving, deleting, and enumerating over directories and subdirectories. In addition to the functionality provided by its base class (FileSystemInfo), DirectoryInfo offers the key members in Table 20-3.

Table 20-3. Key Members of the DirectoryInfo Type

Member

Meaning in Life

Create(), CreateSubdirectory()

Create a directory (or set of subdirectories), given a path name

Delete()

Deletes a directory and all its contents

GetDirectories()

Returns an array of strings that represent all subdirectories in

 

the current directory

GetFiles()

Retrieves an array of FileInfo types that represent a set of files

 

in the given directory

Continued

664 CHAPTER 20 FILE I/O AND ISOLATED STORAGE

Table 20-3. Continued

Member

Meaning in Life

MoveTo()

Moves a directory and its contents to a new path

Parent

Retrieves the parent directory of the specified path

Root

Gets the root portion of a path

 

 

You begin working with the DirectoryInfo type by specifying a particular directory path as a constructor parameter. If you want to obtain access to the current working directory (i.e., the directory of the executing application), use the "." notation. Here are some examples:

//Bind to the current working directory.

DirectoryInfo dir1 = new DirectoryInfo(".");

//Bind to C:\Windows,

//using a verbatim string.

DirectoryInfo dir2 = new DirectoryInfo(@"C:\Windows");

In the second example, you are making the assumption that the path passed into the constructor (C:\Windows) already exists on the physical machine. However, if you attempt to interact with a nonexistent directory, a System.IO.DirectoryNotFoundException is thrown. Thus, if you specify a directory that is not yet created, you will need to call the Create() method before proceeding:

// Bind to a nonexistent directory, then create it.

DirectoryInfo dir3 = new DirectoryInfo(@"C:\MyCode\Testing"); dir3.Create();

Once you have created a DirectoryInfo object, you can investigate the underlying directory contents using any of the properties inherited from FileSystemInfo. To illustrate, create a new Console Application named DirectoryApp. Update your Program class with a new static method that creates a new DirectoryInfo object mapped to C:\Windows (adjust your path if need be) that displays a number of interesting statistics (see Figure 20-2 for output):

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Fun with Directory(Info) *****\n");

ShowWindowsDirectoryInfo();

Console.ReadLine();

}

static void ShowWindowsDirectoryInfo()

{

// Dump directory information.

DirectoryInfo dir = new DirectoryInfo(@"C:\Windows"); Console.WriteLine("***** Directory Info *****");

Console.WriteLine("FullName: {0}", dir.FullName); Console.WriteLine("Name: {0}", dir.Name); Console.WriteLine("Parent: {0}", dir.Parent); Console.WriteLine("Creation: {0}", dir.CreationTime); Console.WriteLine("Attributes: {0}", dir.Attributes); Console.WriteLine("Root: {0}", dir.Root); Console.WriteLine("**************************\n");

}

}

CHAPTER 20 FILE I/O AND ISOLATED STORAGE

665

Figure 20-2. Information about your Windows directory

Enumerating Files with the DirectoryInfo Type

In addition to obtaining basic details of an existing directory, you can extend the current example to use some methods of the DirectoryInfo type. First, let’s leverage the GetFiles() method to obtain information about all *.jpg files located under the C:\Windows\Web\Wallpaper directory.

Note If your machine does not have a C:\Windows\Web\Wallpaper directory, retrofit this code to read files of a directory on your machine (for example, to read all *.bmp files from the C:\Windows directory).

This method returns an array of FileInfo types, each of which exposes details of a particular file (full details of the FileInfo type are explored later in this chapter). Assume the following static method of the Program class, called from within Main():

static void DisplayImageFiles()

{

DirectoryInfo dir = new DirectoryInfo(@"C:\Windows\Web\Wallpaper");

//Get all files with a *.jpg extension.

FileInfo[] imageFiles = dir.GetFiles("*.jpg");

//How many were found?

Console.WriteLine("Found {0} *.jpg files\n", imageFiles.Length);

// Now print out info for each file. foreach (FileInfo f in imageFiles)

{

Console.WriteLine("***************************"); Console.WriteLine("File name: {0}", f.Name); Console.WriteLine("File size: {0}", f.Length); Console.WriteLine("Creation: {0}", f.CreationTime); Console.WriteLine("Attributes: {0}", f.Attributes); Console.WriteLine("***************************\n");

}

}

Once you run the application, you see a listing something like that shown in Figure 20-3. (Your image names may vary!)

666 CHAPTER 20 FILE I/O AND ISOLATED STORAGE

Figure 20-3. Image file information

Creating Subdirectories with the DirectoryInfo Type

You can programmatically extend a directory structure using the DirectoryInfo. CreateSubdirectory() method. This method can create a single subdirectory, as well as multiple nested subdirectories, in a single function call. To illustrate, here is a method that extends the directory structure of the application’s install path with some custom subdirectories:

static void ModifyAppDirectory()

{

DirectoryInfo dir = new DirectoryInfo(".");

//Create \MyFolder off application directory. dir.CreateSubdirectory("MyFolder");

//Create \MyFolder2\Data off application directory. dir.CreateSubdirectory(@"MyFolder2\Data");

}

If you call this method from within Main() and examine your Windows directory using Windows Explorer, you will see that the new subdirectories are present and accounted for (see Figure 20-4).

Although you are not required to capture the return value of the CreateSubdirectory() method, be aware that a DirectoryInfo type representing the newly created item is passed back on successful execution. Consider the following update:

static void ModifyAppDirectory()

{

DirectoryInfo dir = new DirectoryInfo(".");

//Create \MyFolder off initial directory. dir.CreateSubdirectory("MyFolder");

//Capture returned DirectoryInfo object.

DirectoryInfo myDataFolder = dir.CreateSubdirectory(@"MyFolder2\Data");

// Prints path to ..\MyFolder2\Data.

Console.WriteLine("New Folder is: {0}", myDataFolder);

}

CHAPTER 20 FILE I/O AND ISOLATED STORAGE

667

Figure 20-4. Creating subdirectories

Working with the Directory Type

Now that you have seen the DirectoryInfo type in action, you can learn about the Directory type. For the most part, the static members of Directory mimic the functionality provided by the instance-level members defined by DirectoryInfo. Recall, however, that the members of Directory typically return string types rather than strongly typed FileInfo/DirectoryInfo types.

To illustrate some functionality of the Directory type, this final helper function displays the names of all drives mapped to the current computer (via the Directory.GetLogicalDrives() method) and uses the static Directory.Delete() method to remove the \MyFolder and \MyFolder2\Data subdirectories previously created:

static void FunWithDirectoryType()

{

//List all drives on current computer. string[] drives = Directory.GetLogicalDrives(); Console.WriteLine("Here are your drives:"); foreach (string s in drives)

Console.WriteLine("--> {0} ", s);

//Delete what was created.

Console.WriteLine("Press Enter to delete directories"); Console.ReadLine();

try

{

Directory.Delete(string.Format(@"{0}\MyFolder",

Environment.CurrentDirectory));

//The second parameter specifies whether you

//wish to destroy any subdirectories.

Directory.Delete(string.Format(@"{0}\MyFolder2", Environment.CurrentDirectory), true);

}

catch (IOException e)

{

Console.WriteLine(e.Message);

}

}

668 CHAPTER 20 FILE I/O AND ISOLATED STORAGE

Source Code The DirectoryApp project is located under the Chapter 20 subdirectory.

Working with the DriveInfo Class Type

The System.IO namespace provides a class named DriveInfo. Like Directory.GetLogicalDrives(), the static DriveInfo.GetDrives() method allows you to discover the names of a machine’s drives. Unlike Directory.GetLogicalDrives(), however, DriveInfo provides numerous other details (such as the drive type, available free space, volume label, and whatnot). Consider the following Program class defined within a new Console Application named DriveInfoApp:

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Fun with DriveInfo *****\n");

//Get info regarding all drives.

DriveInfo[] myDrives = DriveInfo.GetDrives();

//Now print drive stats.

foreach(DriveInfo d in myDrives)

{

Console.WriteLine("Name: {0}", d.Name); Console.WriteLine("Type: {0}", d.DriveType);

// Check to see whether the drive is mounted. if (d.IsReady)

{

Console.WriteLine("Free space: {0}", d.TotalFreeSpace); Console.WriteLine("Format: {0}", d.DriveFormat); Console.WriteLine("Label: {0}", d.VolumeLabel); Console.WriteLine();

}

}

Console.ReadLine();

}

}

Figure 20-5 shows some possible output.

Figure 20-5. Gather drive details via DriveInfo.

CHAPTER 20 FILE I/O AND ISOLATED STORAGE

669

At this point, you have investigated some core behaviors of the Directory, DirectoryInfo, and DriveInfo classes. Next, you’ll learn how to create, open, close, and destroy the files that populate a given directory.

Source Code The DriveInfoApp project is located under the Chapter 20 subdirectory.

Working with the FileInfo Class

As shown in the previous DirectoryApp example, the FileInfo class allows you to obtain details regarding existing files on your hard drive (time created, size, file attributes, and so forth) and aids in the creation, copying, moving, and destruction of files. In addition to the set of functionality inherited by FileSystemInfo are some core members unique to the FileInfo class, which are described in Table 20-4.

Table 20-4. FileInfo Core Members

Member

Meaning in Life

AppendText()

Creates a StreamWriter type (described later) that appends text to a file

CopyTo()

Copies an existing file to a new file

Create()

Creates a new file and returns a FileStream type (described later) to interact with

 

the newly created file

CreateText()

Creates a StreamWriter type that writes a new text file

Delete()

Deletes the file to which a FileInfo instance is bound

Directory

Gets an instance of the parent directory

DirectoryName

Gets the full path to the parent directory

Length

Gets the size of the current file or directory

MoveTo()

Moves a specified file to a new location, providing the option to specify a new

 

file name

Name

Gets the name of the file

Open()

Opens a file with various read/write and sharing privileges

OpenRead()

Creates a read-only FileStream

OpenText()

Creates a StreamReader type (described later) that reads from an existing text file

OpenWrite()

Creates a write-only FileStream type

 

 

It is important to understand that a majority of the methods of the FileInfo class return a specific I/O-centric object (FileStream, StreamWriter, and so forth) that allows you to begin reading and writing data to (or reading from) the associated file in a variety of formats. You will check out these types in just a moment; however, before you see a working example, let’s examine various ways to obtain a file handle using the FileInfo class type.

The FileInfo.Create() Method

The first way you can create a file handle is to make use of the FileInfo.Create() method:

670CHAPTER 20 FILE I/O AND ISOLATED STORAGE

static void Main(string[] args)

{

//Make a new file on the C drive.

FileInfo f = new FileInfo(@"C:\Test.dat"); FileStream fs = f.Create();

//Use the FileStream object...

//Close down file stream.

fs.Close();

}

Notice that the FileInfo.Create() method returns a FileStream type, which exposes synchronous and asynchronous write/read operations to/from the underlying file (more details in a moment). Be aware that the FileStream object returned by FileInfo.Create() grants full read/write access to all users.

Also notice that after we are done with the current FileStream object, we make sure to close down the handle to release the underlying unmanaged resources of the stream. Given that FileStream implements IDisposable, you can make use of the C# using scope to allow the compiler to generate the teardown logic (see Chapter 8 for details):

static void Main(string[] args)

{

//Defining a 'using scope' for file I/O

//types is ideal.

FileInfo f = new FileInfo(@"C:\Test.dat"); using (FileStream fs = f.Create())

{

// Use the FileStream object...

}

}

The FileInfo.Open() Method

You can use the FileInfo.Open() method to open existing files as well as create new files with far more precision than FileInfo.Create(), given that Open() typically takes several parameters to qualify the overall structure of the file you are manipulating. Once the call to Open() completes, you are returned a FileStream object. Consider the following logic:

static void Main(string[] args)

{

// Make a new file via FileInfo.Open().

FileInfo f2 = new FileInfo(@"C:\Test2.dat"); using(FileStream fs2 = f2.Open(FileMode.OpenOrCreate,

FileAccess.ReadWrite, FileShare.None))

{

// Use the FileStream object...

}

}

This version of the overloaded Open() method requires three parameters. The first parameter specifies the general flavor of the I/O request (e.g., make a new file, open an existing file, append to a file, etc.), which is specified using the FileMode enumeration (see Table 20-5 for details):

public enum FileMode

{

CreateNew,

CHAPTER 20 FILE I/O AND ISOLATED STORAGE

671

Create,

Open,

OpenOrCreate,

Truncate, Append

}

Table 20-5. Members of the FileMode Enumeration

Member

Meaning in Life

CreateNew

Informs the OS to make a new file. If it already exists, an IOException is thrown.

Create

Informs the OS to make a new file. If it already exists, it will be overwritten.

Open

Opens an existing file. If the file does not exist, a FileNotFoundException is

 

thrown.

OpenOrCreate

Opens the file if it exists; otherwise, a new file is created.

Truncate

Opens a file and truncates the file to 0 bytes in size.

Append

Opens a file, moves to the end of the file, and begins write operations (this flag

 

can only be used with a write-only stream). If the file does not exist, a new file is

 

created.

 

 

The second parameter, a value from the FileAccess enumeration, is used to determine the read/write behavior of the underlying stream:

public enum FileAccess

{

Read,

Write, ReadWrite

}

Finally, you have the third parameter, FileShare, which specifies how the file is to be shared among other file handlers. Here are the core names:

public enum FileShare

{

None,

Read,

Write, ReadWrite

}

The FileInfo.OpenRead() and FileInfo.OpenWrite() Methods

While the FileInfo.Open() method allows you to obtain a file handle in a very flexible manner, the FileInfo class also provides members named OpenRead() and OpenWrite(). As you might imagine, these methods return a properly configured read-only or write-only FileStream type, without the need to supply various enumeration values. Like FileInfo.Create() and FileInfo.Open(), OpenRead() and OpenWrite() return a FileStream object (do note that the following code assumes you have files named Test3.dat and Test4.dat on your C drive):

static void Main(string[] args)

{

// Get a FileStream object with read-only permissions.