Chapter 18
Files

Wrox.com Code Downloads for this Chapter

You can find the wrox.com code downloads for this chapter at www.wrox.com/go/beginningvisualc#2015programming on the Download Code tab. The code is in the Chapter 18 download and program names match the names used in the examples throughout the chapter.

Files can be a great way to store data between instances of your application, or they can be used to transfer data between applications. User and application configuration settings can be stored to be retrieved the next time your application is run.

This chapter shows you how to use files effectively in your applications, touching on the major classes used to create, read from, and write to files, and the supporting classes used to manipulate the file system from C# code. Although you won't examine all of the classes in detail, this chapter goes into enough depth to give you a good idea of the concepts and fundamentals.

File Classes for Input and Output

Reading and writing files is an essential way to get data into your C# program (input) and send data out of your program (output). Because files are used for input and output, the file classes are contained in the System.IO namespace. (IO is a common abbreviation for Input/Output.)

System.IO contains the classes for reading and writing data to and from files, and you can reference this namespace in your C# application to gain access to these classes without fully qualifying type names.

The classes covered in this chapter are described in Table 18.1.

Table 18.1 File System Access Classes

Class Description
File A static utility class that exposes many static methods for moving, copying, and deleting files.
Directory A static utility class that exposes many static methods for moving, copying, and deleting directories.
Path A utility class used to manipulate path names.
FileInfo Represents a physical file on disk, and has methods to manipulate this file. For any reading from and writing to the file, a Stream object must be created.
DirectoryInfo Represents a physical directory on disk and has methods to manipulate this directory.
FileSystemInfo Serves as the base class for both FileInfo and DirectoryInfo, making it possible to deal with files and directories at the same time using polymorphism.
FileSystemWatcher The most advanced class you examine in this chapter. It is used to monitor files and directories, and it exposes events that your application can catch when changes occur in these locations.

You'll also look at the System.IO.Compression namespace, which enables you to read from and write to compressed files. In particular, you will look at the following two stream classes:

  • DeflateStream — Represents a stream in which data is compressed automatically when writing, or uncompressed automatically when reading. Compression is achieved using the Deflate algorithm.
  • GZipStream — Represents a stream in which data is compressed automatically when writing, or uncompressed automatically when reading. Compression is achieved using the GZIP (GNU Zip) algorithm.

The File and Directory Classes

The File and Directory utility classes expose many static methods for manipulating, surprisingly enough, files and directories. These methods make it possible to move files, query and update attributes, and create FileStream objects. As you learned in Chapter 8, static methods can be called on classes without having to create instances of them.

Some of the most useful static methods of the File class are shown in the Table 18.2.

Table 18.2 Static Methods of the File Class

Method Description
Copy() Copies a file from a source location to a target location.
Create() Creates a file in the specified path.
Delete() Deletes a file.
Open() Returns a FileStream object at the specified path.
Move() Moves a specified file to a new location. You can specify a different name for the file in the new location.

Some useful static methods of the Directory class are shown in Table 18.3.

Table 18.3 Static Methods of the Directory Class

Method Description
CreateDirectory() Creates a directory with the specified path.
Delete() Deletes the specified directory and all the files within it.
GetDirectories() Returns an array of string objects that represent the names of the directories below the specified directory.
EnumerateDirectories() Like GetDirectories(), but returns an IEnumerable<string> collection of directory names.
GetFiles() Returns an array of string objects that represent the names of the files in the specified directory.
EnumerateFiles() Like GetFiles(), but returns an IEnumerable<string> collection of filenames.
GetFileSystemEntries() Returns an array of string objects that represent the names of the files and directories in the specified directory.
EnumerateFileSystemEntries() Like GetFileSystemEntries(), but returns an IEnumerable<string> collection of file and directory names.
Move() Moves the specified directory to a new location. You can specify a new name for the folder in the new location.

The three EnumerateXxx() methods provide better performance than their GetXxx() counterparts when a large amount of files or directories exist.

The FileInfo Class

Unlike the File class, the FileInfo class is not static and does not have static methods. This class is useful only when instantiated. A FileInfo object represents a file on a disk or a network location, and you can create one by supplying a path to a file:

FileInfo aFile = new FileInfo(@"C:Log.txt");

You can also pass the name of a directory to the FileInfo constructor, although in practical terms that isn't particularly useful. Doing this causes the base class of FileInfo, which is FileSystemInfo, to be initialized with all the directory information, but none of the FileInfo methods or properties relating specifically to files will work.

Many of the methods exposed by the FileInfo class are similar to those of the File class, but because File is a static class, it requires a string parameter that specifies the file location for every method call. Therefore, the following calls do the same thing:

FileInfo aFile = new FileInfo("Data.txt");
if (aFile.Exists)
   WriteLine("File Exists");
if (File.Exists("Data.txt"))
   WriteLine("File Exists");

In this code, a check is made to see whether the file Data.txt exists. Note that no directory information is specified here, which means that the current working directory is the only location examined. This directory is the one containing the application that calls this code. You'll look at this in more detail a little later, in the section “Path Names and Relative Paths.”

Most of the FileInfo methods mirror the File methods in this manner. In most cases it doesn't matter which technique you use, although the following criteria can help you to decide which is more appropriate:

  • It makes sense to use methods on the static File class if you are making only a single method call — the single call will be faster because the .NET Framework won't have to go through the process of instantiating a new object and then calling the method.
  • If your application is performing several operations on a file, then it makes more sense to instantiate a FileInfo object and use its methods — this saves time because the object will already be referencing the correct file on the file system, whereas the static class has to find it every time.

The FileInfo class also exposes properties relating to the underlying file, some of which can be manipulated to update the file. Many of these properties are inherited from FileSystemInfo, and thus apply to both the FileInfo and DirectoryInfo classes. The properties of FileSystemInfo are shown in Table 18.4.

Table 18.4 FileSystemInfo Properties

Property Description
Attributes Gets or sets the attributes of the current file or directory, using the FileAttributes enumeration.
CreationTime, CreationTimeUtc Gets or sets the creation date and time of the current file, available in coordinated universal time (UTC) and non-UTC versions.
Extension Retrieves the extension of the file. This property is read-only.
Exists Determines whether a file exists. This is a read-only abstract property, and is overridden in FileInfo and DirectoryInfo.
FullName Retrieves the full path of the file. This property is read-only.
LastAccessTime, LastAccessTimeUtc Gets or sets the date and time that the current file was last accessed, available in UTC and non-UTC versions.
LastWriteTime, LastWriteTimeUtc Gets or sets the date and time that the current file was last written to, available in UTC and non-UTC versions.
Name Retrieves the full path of the file. This is a read-only abstract property, and is overridden in FileInfo and DirectoryInfo.

The properties specific to FileInfo are shown in Table 18.5.

Table 18.5 FileInfo Properties

Property Description
Directory Retrieves a DirectoryInfo object representing the directory containing the current file. This property is read-only.
DirectoryName Returns the path to the file's directory. This property is read-only.
IsReadOnly Shortcut to the read-only attribute of the file. This property is also accessible via Attributes.
Length Gets the size of the file in bytes, returned as a long value. This property is read-only.

The DirectoryInfo Class

The DirectoryInfo class works exactly like the FileInfo class. It is an instantiated object that represents a single directory on a machine. Like the FileInfo class, many of the method calls are duplicated across Directory and DirectoryInfo. The guidelines for choosing whether to use the methods of File or FileInfo also apply to DirectoryInfo methods:

  • If you are making a single call, use the static Directory class.
  • If you are making a series of calls, use an instantiated DirectoryInfo object.

The DirectoryInfo class inherits most of its properties from FileSystemInfo, as does FileInfo, although these properties operate on directories instead of files. There are also two DirectoryInfo-specific properties, shown in Table 18.6.

Table 18.6 Properties Unique to the DirectoryInfo Class

Property Description
Parent Retrieves a DirectoryInfo object representing the directory containing the current directory. This property is read-only.
Root Retrieves a DirectoryInfo object representing the root directory of the current volume — for example, the C: directory. This property is read-only.

Path Names and Relative Paths

When specifying a path name in .NET code, you can use absolute or relative path names. An absolute path name explicitly specifies a file or directory from a known location — such as the C: drive. An example of this is C:WorkLogFile.txt — this path defines exactly where the file is, with no ambiguity.

Relative path names are relative to a starting location. By using relative path names, no drive or known location needs to be specified. You saw this earlier, where the current working directory was the starting point, which is the default behavior for relative path names. For example, if your application is running in the C:DevelopmentFileDemo directory and uses the relative path LogFile.txt, the file references would be C:DevelopmentFileDemoLogFile.txt. To move “up” a directory, the .. string is used. Thus, in the same application, the path ..Log.txt points to the file C:DevelopmentLog.txt.

As shown earlier, the working directory is initially set to the directory in which your application is running. When you are developing with Visual Studio, this means the application is several directories beneath the project folder you created. It is usually located in ProjectNameinDebug. To access a file in the root folder of the project, then, you have to move up two directories with ..... You will see this happen often throughout the chapter.

Should you need to, you can determine the working directory by using Directory.GetCurrentDirectory(), or you can set it to a new path by using Directory.SetCurrentDirectory().

Streams

All input and output in the .NET Framework involves the use of streams. A stream is an abstract representation of a serial device. A serial device is something that stores and/or accesses data in a linear manner, that is, one byte at a time, sequentially. This device can be a disk file, a network channel, a memory location, or any other object that supports linear reading, writing, or both. By keeping the device abstract, the underlying destination/source of the stream can be hidden. This level of abstraction enables code reuse, and enables you to write more generic routines because you don't have to worry about the specifics of how data transfer actually occurs. Therefore, similar code can be transferred and reused when the application is reading from a file input stream, a network input stream, or any other kind of stream. Because you can ignore the physical mechanics of each device, you don't need to worry about, for example, hard disk heads or memory allocation when dealing with a file stream.

A stream can represent almost any source such as a keyboard, a physical disk file, a network location, a printer, or even another program, but this chapter focuses on reading and writing disk files. The concepts applied to reading/writing disk files apply to most devices, so you'll gain a basic understanding of streams and learn a proven approach that can be applied to many situations.

Classes for Using Streams

The classes for using streams are contained in the same System.IO namespace along with the File and Directory classes. These classes are listed in Table 18.7.

Table 18.7 Stream Classes

Class Description
FileStream Represents a file that can be written to, read from, or both. This file can be written to and read from asynchronously or synchronously.
StreamReader Reads character data from a stream and can be created by using a FileStream as a base.
StreamWriter Writes character data to a stream and can be created by using a FileStream as a base.

Let's look now at how to use each of these classes.

The FileStream Object

The FileStream object represents a stream pointing to a file on a disk or a network path. Although the class does expose methods for reading and writing bytes from and to the files, most often you will use a StreamReader or StreamWriter to perform these functions. That's because the FileStream class operates on bytes and byte arrays, whereas the Stream classes operate on character data. Character data is easier to work with, but certain operations, such as random file access (access to data at some point in the middle of a file), can be performed only by a FileStream object. You'll learn more about this later in the chapter.

There are several ways to create a FileStream object. The constructor has many different overloads, but the simplest takes just two arguments: the filename and a FileMode enumeration value:

FileStream aFile = new FileStream(filename, FileMode.<Member>);

The FileMode enumeration has several members that specify how the file is opened or created. You'll see the possibilities shortly. Another commonly used constructor is as follows:

FileStream aFile =
new FileStream(filename, FileMode.<Member>, FileAccess.<Member>);

The third parameter is a member of the FileAccess enumeration and is a way of specifying the purpose of the stream. The members of the FileAccess enumeration are shown in Table 18.8.

Table 18.8 FileAccess Enumeration Members

Member Description
Read Opens the file for reading only
Write Opens the file for writing only
ReadWrite Opens the file for reading or writing

Attempting to perform an action other than that specified by the FileAccess enumeration member will result in an exception being thrown. This property is often used as a way to vary user access to the file based on the user's authorization level.

In the version of the FileStream constructor that doesn't use a FileAccess enumeration parameter, the default value is used, which is FileAccess.ReadWrite.

The FileMode enumeration members are shown in Table 18.9. What actually happens when each of these values is used depends on whether the filename specified refers to an existing file. Note that the entries in this table refer to the position in the file that the stream points to when it is created, a topic you'll learn more about in the next section. Unless otherwise stated, the stream points to the beginning of a file.

Table 18.9 FileMode Enumeration Members

Member File Exists Behavior No File Exists Behavior
Append The file is opened, with the stream positioned at the end of the file. Can be used only in conjunction with FileAccess.Write. A new file is created. Can be used only in conjunction with FileAccess.Write.
Create The file is destroyed, and a new file is created in its place. A new file is created.
CreateNew An exception is thrown. A new file is created.
Open The file is opened, with the stream positioned at the beginning of the file. An exception is thrown.
OpenOrCreate The file is opened, with the stream positioned at the beginning of the file. A new file is created.
Truncate The file is opened and erased. The stream is positioned at the beginning of the file. The original file creation date is retained. An exception is thrown.

Both the File and FileInfo classes expose OpenRead() and OpenWrite() methods that make it easier to create FileStream objects. The first opens the file for read-only access, and the second allows write-only access. These methods provide shortcuts, so you do not have to provide all the information required in the form of parameters to the FileStream constructor. For example, the following line of code opens the Data.txt file for read-only access:

FileStream aFile = File.OpenRead("Data.txt");

The following code performs the same function:

FileInfo aFileInfo = new FileInfo("Data.txt");
FileStream aFile = aFileInfo.OpenRead();

File Position

The FileStream class maintains an internal file pointer that points to the location within the file where the next read or write operation will occur. In most cases, when a file is opened, it points to the beginning of the file, but this pointer can be modified. This enables an application to read or write anywhere within the file, which in turn enables random access to a file and the capability to jump directly to a specific location in the file. This can save a lot of time when dealing with very large files because you can instantly move to the location you want.

The method that implements this functionality is the Seek() method, which takes two parameters. The first parameter specifies how far to move the file pointer, in bytes. The second parameter specifies where to start counting from, in the form of a value from the SeekOrigin enumeration. The SeekOrigin enumeration contains three values: Begin, Current, and End.

For example, the following line would move the file pointer to the eighth byte in the file, starting from the very first byte in the file:

aFile.Seek(8, SeekOrigin.Begin);

The following line would move the file pointer two bytes forward, starting from the current position. If this were executed directly after the previous line, then the file pointer would now point to the tenth byte in the file:

aFile.Seek(2, SeekOrigin.Current);

When you read from or write to a file, the file pointer changes as well. After you have read 10 bytes, the file pointer will point to the byte after the tenth byte read.

You can also specify negative seek positions, which could be combined with the SeekOrigin.End enumeration value to seek near the end of the file. The following seeks to the fifth byte from the end of the file:

aFile.Seek(-5, SeekOrigin.End);

Files accessed in this manner are sometimes referred to as random access files because an application can access any position within the file. The StreamReader and StreamWriter classes described later access files sequentially and do not allow you to manipulate the file pointer in this way.

Reading Data

Reading data using the FileStream class is not as easy as using the StreamReader class, which you will look at later in this chapter. That's because the FileStream class deals exclusively with raw bytes. Working in raw bytes makes the FileStream class useful for any kind of data file, not just text files. By reading byte data, the FileStream object can be used to read files such as images or sound files. The cost of this flexibility is that you cannot use a FileStream to read data directly into a string as you can with the StreamReader class. However, several conversion classes make it fairly easy to convert byte arrays into character arrays, and vice versa.

The FileStream.Read() method is the primary means to access data from a file that a FileStream object points to. This method reads the data from a file and then writes this data into a byte array. There are three parameters, the first being a byte array passed in to accept data from the FileStream object. The second parameter is the position in the byte array to begin writing data to — this is normally zero, to begin writing data from the file at the beginning of the array. The last parameter specifies how many bytes to read from the file.

The following Try It Out demonstrates reading data from a random access file. The file you will read from is actually the class file you create for the example.

Writing Data

The process for writing data to a random access file is very similar; a byte array must be created. The easiest way to do this is to first build the character array you want to write to the file. Next, use the Encoder object to convert it to a byte array, very much as you used the Decoder object. Last, call the Write() method to send the array to the file.

Here's a simple example to demonstrate how this is done.

The StreamWriter Object

Working with arrays of bytes is not most people's idea of fun — having worked with the FileStream object, you might be wondering whether there is an easier way. Fear not, for once you have a FileStream object, you will usually create a StreamWriter or StreamReader and use its methods to manipulate the file. If you don't need the capability to change the file pointer to any arbitrary position, these classes make working with files much easier.

The StreamWriter class enables you to write characters and strings to a file, with the class handling the underlying conversions and writing to the FileStream object for you.

There are many ways to create a StreamWriter object. If you already have a FileStream object, then you can use it to create a StreamWriter:

FileStream aFile = new FileStream("Log.txt", FileMode.CreateNew);
StreamWriter sw = new StreamWriter(aFile);

A StreamWriter object can also be created directly from a file:

StreamWriter sw = new StreamWriter("Log.txt", true);

This constructor takes the filename and a Boolean value that specifies whether to append to the file or create a new one:

  • If this is set to false, then a new file is created or the existing file is truncated and then opened.
  • If it is set to true, then the file is opened and the data is retained. If there is no file, then a new one is created.

Unlike creating a FileStream object, creating a StreamWriter does not provide you with a similar range of options — other than the Boolean value to append or create a new file, you have no option for specifying the FileMode property as you did with the FileStream class. Nor do you have an option to set the FileAccess property, so you will always have read/write privileges to the file. To use any of the advanced parameters, you must first specify them in the FileStream constructor and then create a StreamWriter from the FileStream object, as you do in the following Try It Out.

The StreamReader Object

Input streams are used to read data from an external source. Often, this will be a file on a disk or network location, but remember that this source could be almost anything that can send data, such as a network application or even the Console.

The StreamReader class is the one that you will be using to read data from files. Like the StreamWriter class, this is a generic class that can be used with any stream. In the next Try It Out, you again construct it around a FileStream object so that it points to the correct file.

StreamReader objects are created in much the same way as StreamWriter objects. The most common way to create one is to use a previously created FileStream object:

FileStream aFile = new FileStream("Log.txt", FileMode.Open);
StreamReader sr = new StreamReader(aFile);

Like StreamWriter, the StreamReader class can be created directly from a string containing the path to a particular file:

StreamReader sr = new StreamReader("Log.txt");

Reading Data

The ReadLine() method is not the only way you can access data in a file. The StreamReader class has many methods for reading data.

The simplest of the reading methods is Read(). It returns the next character from the stream as a positive integer value or a -1 if it has reached the end. This value can be converted into a character by using the Convert utility class. In the preceding example, the main parts of the program could be rewritten as follows:

StreamReader sr = new StreamReader(aFile);
int charCode;
charCode = sr.Read();
while(charCode != -1)
{
   Write(Convert.ToChar(charCode));
   charCode = sr.Read();
}
sr.Close();

A very convenient method to use with smaller files is the ReadToEnd() method. It reads the entire file and returns it as a string. In this case, the earlier application could be simplified to the following:

StreamReader sr = new StreamReader(aFile);
line = sr.ReadToEnd();
WriteLine(line);
sr.Close();

Although this might seem easy and convenient, be careful. By reading all the data into a string object, you are forcing the data in the file to exist in memory. Depending on the size of the data file, this can be prohibitive. If the data file is extremely large, then it is better to leave the data in the file and access it with the methods of the StreamReader.

Another way to deal with large files, which was introduced in .NET 4, is to use the static File.ReadLines() method. There are, in fact, several static methods of File that you can use to simplify reading and writing file data, but this one is particularly interesting in that it returns an IEnumerable<string> collection. You can iterate through the strings in this collection to read the file one line at a time. Using this method, you can rewrite the previous example as follows:

foreach (string alternativeLine in File.ReadLines("Log.txt"))
   WriteLine(alternativeLine);

There are, as you can see, several ways in .NET to achieve the same result — namely, reading data from a file. Choose the technique that suits you best.

Asynchronous File Access

Sometimes — for example, when you are performing a lot of file access operations in one go or are working with very large files — reading and writing file system data can be slow. If this is the case, you might want to perform other operations while you wait. This is especially important with desktop applications, where you want your application to remain responsive to users while you are doing work in the background.

To facilitate this, .NET 4.5 introduced asynchronous ways to work with streams. This applies to the FileStream class, as well as to StreamReader and StreamWriter. If you have browsed through the definitions of these classes, you might have noticed some methods that end with the suffix Async — for example, StreamReader has a method called ReadLineAsync(), which is an asynchronous version of ReadLine(). These methods are designed to be used with the task-based asynchronous programming model.

Asynchronous programming is an advanced technique that isn't covered in detail in this book. However, if asynchronous file system access is something you are interested in doing then this is the place to start. You might also want to read Professional C# 5.0 and .NET 4.5.1 by Christian Nagel, Jay Glynn, and Morgan Skinner (Wrox, 2014) for more details.

Reading and Writing Compressed Files

Often when dealing with files, quite a lot of space is used up on the hard disk. This is particularly true for graphics and sound files. You've probably come across utilities that enable you to compress and decompress files, which are handy when you want to move them around or e-mail them. The System.IO.Compression namespace contains classes that enable you to compress files from your code, using either the GZIP or Deflate algorithm — both of which are publicly available and free for anyone to use.

There is a little bit more to compressing files than just compressing them, though. You've probably seen how commercial applications enable multiple files to be placed in a single compressed file, often called an archive. There are classes in the System.IO.Compression namespace that enable similar functionality. However, to keep things simple for this book you'll just look at one scenario: saving text data to a compressed file. You are unlikely to be able to access this file in an external utility, but the file will be much smaller than its uncompressed equivalent!

The two compression stream classes in the System.IO.Compression namespace that you'll look at here, DeflateStream and GZipStream, work very similarly. In both cases, you initialize them with an existing stream, which, in the case of files, will be a FileStream object. After this you can use them with StreamReader and StreamWriter just like any other stream. All you need to specify in addition to that is whether the stream will be used for compression (saving files) or decompression (loading files) so that the class knows what to do with the data that passes through it. This is best illustrated with the following example.

Monitoring the File System

Sometimes an application must do more than just read and write files to the file system. For example, it might be important to know when files or directories are being modified. The .NET Framework has made it easy to create custom applications that do just that.

The class that helps you to do this is the FileSystemWatcher class. It exposes several events that your application can catch. This enables your application to respond to file system events.

The basic procedure for using the FileSystemWatcher is simple. First, you must set a handful of properties, which specify where to monitor, what to monitor, and when it should raise the event that your application will handle. Then you give it the addresses of your custom event handlers, so that it can call these when significant events occur. Finally, you turn it on and wait for the events.

The properties that must be set before a FileSystemWatcher object is enabled are shown in Table 18.10.

Table 18.10 FileSystemWatcher Properties

Property Description
Path Must be set to the file location or directory to monitor.
NotifyFilter A combination of NotifyFilters enumeration values that specify what to watch for within the monitored files. These represent properties of the file or folders being monitored. If any of the specified properties change, then an event is raised. The possible enumeration values are Attributes, CreationTime, DirectoryName, FileName, LastAccess, LastWrite, Security, and Size. Note that these can be combined using the binary OR operator.
Filter A filter specifying which files to monitor — for example, *.txt.

Once these are set, you must write event handlers for four events: Changed, Created, Deleted, and Renamed. As shown in Chapter 13, this is simply a matter of creating your own method and assigning it to the object's event. By assigning your own event handler to these methods, your method will be called when the event is fired. Each event will fire when a file or directory matching the Path, NotifyFilter, and Filter property is modified.

Once you have set the properties and the events, set the EnableRaisingEvents property to true to begin the monitoring. In the following Try It Out, you use FileSystemWatcher in a simple client application to keep tabs on a directory of your choice.

image What You Learned in This Chapter

Topic Key Concepts
Streams A stream is an abstract representation of a serial device that you can read from or write to a byte at a time. Files are an example of such a device. There are two types of streams — input and output — for reading from and writing to devices, respectively.
File classes There are numerous classes in the .NET Framework that abstract file system access, including File and Directory for dealing with files and directories through static methods, and FileInfo and DirectoryInfo, which can be instantiated to represent specific files and directories. The latter pair of classes is useful when you perform multiple operations on files and directories, as those classes don't require a path for every method call. Typical operations that you can perform on files and directories include interrogating and changing properties, creating, deleting, and copying.
File paths File and directory paths can be absolute or relative. An absolute path gives a complete description of a location starting from the root of the drive that contains it; all parent directories are separated from child directories with backslashes. Relative directories are similar, but start from a defined point in the file system, such as the directory where an application is executing (the working directory). To navigate the file system, you often use the .. parent directory alias.
The FileStream object The FileStream object provides access to the contents of a file, for reading and writing purposes. It accesses file data at the byte level, and so is not always the best choice for accessing file data. A FileStream instance maintains a position byte index within a file so that you can navigate through the contents of a file. Accessing a file at any point in this way is known as random access.
Reading and writing to streams An easier way to read and write file data is to use the StreamReader and StreamWriter classes in combination with a FileStream. These enable you to read and write character and string data rather than working with bytes. These types expose familiar methods for working with strings, including ReadLine() and WriteLine(). Because they work with string data, these classes make it easy to work with comma-delimited files, which are a common way to represent structured data.
Compressed files You can use the DeflateStream and GZipStream compressed stream classes to read and write compressed data from and to files. These classes work with byte data much like FileStream, but as with FileStream you can access data through StreamReader and StreamWriter classes to simplify your code.
Monitoring the file system You can use the FileSystemWatcher class to monitor changes to file system data. You can monitor both files and directories, and provide a filter, if required, to modify only those files that have a specific file extension. FileSystemWatcher instances notify you of changes by raising events that you can handle in your code.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset