Theoretically, it is perfectly possible to use the FileStream class to read in and display text files.
You have, after all, just done that. The format in which the Default. aspx file is displayed in the preceding example is not particularly user-friendly, but that has nothing to do with any intrinsic problem with the FileStream class, only with how you chose to display the results in the text box.
Having said that, if you know that a particular file contains text, you will usually find it more convenient to read and write it using the StreamReader and StreamWriter classes instead of the FileStream class.
That is because these classes work at a slightly higher level and are specifically geared to reading and writing text.
The methods that they implement are able to automatically detect convenient points to stop reading text, based on the contents of the stream.
¤ These classes implement methods to read or write one line of text at a time, StreamReader ReadLine () and Streamwriter.
In the case of reading, this means that the stream will automatically determine for you where the next carriage return is and stop reading at that point.
In the case of writing, it means that the stream will automatically append the carriage return-line feed combination to the text that it writes out.
¤ By using the StreamReader and Strearnwri ter classes, you don’t need to worry about the encoding (the text format) used in the file.
Possible encodings include ASCll (1 byte for each character), or any of the Unicode-based formats, Unicode, UTF7, urF8, and urF32.
Text files on Windows 9x systems are always in ASCII because Windows 9x does not support Unicode; however, because Windows NT, 2000,XP,2003,Vista, and Windows Server 2008 all de support Unicode, text files might theoretically contain Unicode, UTF7, urF8, or urF32 data instead of ASCII data.
The convention is that if the file is in ASCII format, it will simply contain the text.
If it is in any Unicode format, this will be indicated by the first 2 or 3 bytes of the file, which are set to particular combinations of values to indicate the format used in the file.
These bytes are known as the byte code markers. When you open a me using any of the standard Windows applications, such as Notepad or WordPad, you do not need to worry about this because these applications are aware of the different encoding methods and will automatically read the file correctly.
This is also true for the StreamReader class, which will correctly read in a file in any of these formats, and the Streamwriter class is capable of formatting the text it writes out-using whatever encoding technique you request.
If you wanted to read in and display a text file using the FileStream class, however, you would have to handle all of this yourself.
The Stream Reader Class
StreamReader is used to read text files Constructing streamReader is in some ways easier than constructing a FileStream instance because some of the FileStream options are not required when using StreamReader.
In particular, the mode and access types are not relevant to StreamReader because the only thing You can do with a StreamReader is read Furthermore, there is no direct option to specify the sharing permissions.
However, there are a couple of new options:
¤ You need to specify what to do about the different encoding methods.
You can instruct the StreamReader to examine the byte code markers in the beginning of the file to determine the encoding method, or you can simply tell the StreamReader to assume that the file uses a specified encoding method.
¤ Instead of supplying a file name to be read from, you can supply a reference to another stream.
This last option deserves a bit more discussion because it illustrates another advantage of basing the model for reading and writing data on the.concept of streams. Because the StreamReader works at a relatively high level, you might find it useful if you have another stream that is there to read data from some other source, but you would like to use the facilities provided by StreamReader to process that other stream as if it contained text.
You can do so by simply passing the output from this stream to a StreamReader.
In this way, StreamReader can be used to read and process data from any data source – not only files.
This is essentially the situation discussed earlier with regard to the BinaryReader class.
However, in this book you will only use StreamReader to connect directly to files.
The result of these possibilities is that StreamReader has a large number of constructors. Not only that, but there are a couple of Filelnfo methods that return StreamReader references, too: OpenText () and CreateText ( ) .
The following just illustrates some of the constructors,
The simplest constructor takes just a file name.
This StreamReader will examine the byte order marks to determine the encoding:
StreamReader sr = new StreamReader(9″C:\My Documents\ReadMe.txt”);
Alternatively, if you prefer to specify that urF8 encoding should be assumed:
Streamleader sr ~ new StreamReader(@”C:\My Documents\ReadMe.txt”,
You specify the encoding by using one of several properties on a class, System.
Text. Encoding This class is an abstract base class, from which a number of classes are derived and which implements methods that actually perform the text encoding.
Each property returns an instance of the appropriate class, and the possible properties you can use here are:
The following example demonstrates′ hooking up a StreamReader to a FileStream.
The advantage of this is that you can specify whether to create the file and the share permissions, which you cannot do if you directly attach a StreamReader to the file:
FileStream fs = new FileStream(@”C:\My Documents\ReadMe.txt” ,
FileMode ,Open. FileAcces s .Read. Fileshare .None) ;
StreamReader sr = new StreamReader(fs);
For this example, you specify that the StreamReader will look for byte code markers to determine the encoding method used, as it will do in the following examples, in which the StreamReader is obtained from a FileInfo instance:
Filelnfo myFile = new Filelnfo(@”C:\My Documents\ReadMe.txt”);
StreamReader sr = myFile.OpenText();
Just as with a FileS”tream, you should always close a StreamReader after use.
Failure to do so will result m:the file remaining locked to other processes (unless you used a FileStream to construct the StreamReader and specified FileShare.
Now that you have gone to the trouble of instantiating a StreamReader, you can do something with it.
As with the FileStream, you will simply see the various ways to read data, and the other, less commonly used StreamReader methods are left to the SDK documentation.
Possibly the easiest method to use is ReadLine (),.which keeps reading until it gets to the end of a line.
It does not include the carriage return-line feed combination that marks the end of the line in the returned string:
string restOfStream = sr.ReadToEnd();
You can read a single character:
int nextChar = sr.Read();
This overload of Read () casts the returned character to an into This is so that it has the option of returning a value of -1 if the end of the stream has been reached.
Finally, you can read a given number of characters into an array, with an offset:
II to read 100 characters in.
int nChars = 100;
char IJ charArray = new charlnCharsl;
int rtCharsRead = sr.Read(charArray,IO, nChars);
nCharsRead will be less than nChars if you have requested to read more characters than are left in the file.
The Stream Writer Class
This works in the same way as the StreamReader, except that you can use StreamWri ter only to Write to a file (or to another stream).
Possibilities for constructing a StreamWri ter include:
StreamWriter sw = new StreamWriter(@’C:\My Documents\ReadMe.txt’);
This will use UTF8 encoding, which is regarded by .NET as the default encoding method. How want,
you can specify an alternative. encoding:
StreamWriter sw = new StreamWriter(@’c:\My Documents\ReadMe.txt’, true,
In this constructor, the second parameter is a Boolean that indicates whether the file should be opened for appending.
There is, oddly, no constructor that takes only a file name and an encoding class.
Of course, you may want to hook up StreamWriter to a file stream to give you more control over the options for opening the file:
FileStream fs = new FileStream(@’C:\My Documents\ReadMe.txt’,
FileMode.CreateNew, FileAccess.Write, FileShare.Read);
-. StreamWriter sw = new StreamWriter(fs);
Filtrstream does not implement any methods that return a StreamWri ter class.
Alternatively, if you want to create a new file and start writing data to it, you will find this
Filelnfo rnyFile = new Filelnfo(@’C:\My Docurnents\NewFile.txt’);
StreamWriter sw= myFile.CreateText(); .
Just as with all other stream classes, it is important to close a StreamWriter class when you have finished with it:
sw. Close ();
Writing to the stream is done using any of four overloads of streamwinter.
Write ().The Simplest writes out a string and appends it with a carriage return-line feed combination:
string nextLine = ‘Groovy Line’;
It is also possible. write out a single character:
char next Char = ‘a’;
And an array of characters:
char Il charArr•ay = new charl100l.;
II initialize these characters
It is even possible to write out a portion of an array of characters;
int nCharsToWrite = 50;
int startAtLocation = 25;
char  charArray = new char[lOO];
1/ initialize these characters
sw.Write(charArray, startAtLocation, nCharsToWrite);
The ReadWriteText example displays the use of the StreamReader and Streamwriter classes.
It is similar to the earlier ReadBinaryFile example, but it assumes that the file to be read in is a text file and displays it as such It is also capable of saving the file (with any modifications you have made to the text in the text box).
It will save any file in Unicode format.
The screenshot in ReadwriteText displaying the same Default aspx file that you used earlier This time, however, you are able to read the contents a bit more easily!
We won’t cover the details of adding the event handlers for the Open File dialog box, because they are basically the same as in the earlier BinaryFileReader example.
As with that example, opening a new file causes the DisplayFile () method to be called.
The only real difference between this example and the previous one is the implementation of Display FiIe as well as that you now have the option to save file.
This is represented by another menu option, Save The handler for this option calls another method you have added to the code, SaveFile ().
(Note that the new file always overwrites the original file; this example does not have an opticn to write to a different file.)
You will look at SaveFile () first because that is the simplest function. You simply write each line of the text box, in turn, to a Streamwriter stream, relying on the StreamReader.
WriteLine () method append the trailing carriage return and line feed to the end of each line:
StreamWriter sw = new StreamWriter(chosenFile. false. Encoding.Unicode);
void SaveFile ()
foreach (string line in textBoxContents.Lines)
chosenFile is a string field of the main form, which contains the name of the file you have read in (just as for the previous example).
Notice that you specify Unicode encoding when you open the stream If you wanted to write files in some other format, you would simply need to change the value of this parameter.
The second parameter to this constructor would be set to true if you wanted to append to a file, but you do not in this case.
The encoding must be set at construction time for a StreamWriter It is subsequently available as a read-only property, Encoding.
Now you examine how files are read in The process of reading in-is complicated by the fact that you don’t know how many lines it is going to contain until you have read in the file.
For example, you don’t know how many (char) 13 (char) 10 sequences are in the file because char (13)char (10) is the carriage return-line feed combination that occurs at the end of a line.
You solve this problem by initially reading the file into an instance of the StringCollection class, which is in the System.
Collections, Specialized namespace this class is designed to hold a set of strings that can be dynamically expanded.
It implements two methods that you will be Interested in: Add( ), which adds a string to the collection, and Copy To(), which copies the string collection into a normal array (a System .Array instance).
Each element of the StringCollection object will hold one line of the file the DisplayFile () method calls another method, ReadFileIntoStringCollection (), which actually reads in the file.
After doing this, you now know how many lines there are, so you lire in a position to copy the StringCollection into a normal, fixed-size array and feed this array into the text box.
Because only the references to the strings, not the strings themselves, are copied when you actually make the copy, the process is reasonably efficient:
StringCollection line.Collection = ReadFilelntoStringCollection();
string [I line.Array = new .tring[line.Collection.Countll
line.Collection.CopyTo(line.Array, 0) 1
thi =. textBoxContent =. Line = line.Array;
The second parameter of StringCollection. CopyTo() indicates the index within the destination lIrray of where you want the collection to start.
Now you examine the ReadFilelntoStringCollection (I method.
You StreamReader to read ll’\ each line. The main complication here is the need to count the characters read in to make sure that you do not exceed the capacity of the text box: StringCollection ReadFilelntoStringCollection ()
con.t int HaxByte.. 655361
StreamReader sr = new StreamReader(chosenFile):
StringCollection result = new StringCollection() :
int nBytesRead = 0;
while ( (nextLine = sr.ReadLine()) != null)
nBytesRead += nextLine.Length;
if (nBytesRead > MaxBytes)
result. Add (nextLine) ;
That completes the code for this example If you run ReadWriteText, read in the Default. aspx file, and then save it, the file will be in Unicode format.
You would not be able to tell this from any of the usual Windows applications Notepad, WordPad, and even the ReadWriteText example will still read the file in and display it correctly under Windows NT/2000/XP /2003/Vista/2008, although, because Windows 9x doesn’t support Unicode, applications like Notepad won’t be able to understand the Unicode file on those platforms.
(If you download the example from the csharpaid Press web site at www.csharpaid.com
you can try this!) However, if you try to display the file again using the earlier Binary FileReader example, you can see the difference immediately, The two initial bytes that indicate the file is in Unicodem, format are visible, and thereafter you see that every character is represented by 2 bytes.
This last fact is obvious because the high-order byte of every character in this particular file is zero, so every second byte·
in this file now displays xOO.
Reading Drive Information
In addition to working with files and directories, the .NET Framework includes the ability to read information from a specified drive.
This is done using the DriveInfo class, The DriveInfo class can perform a scan of a system to provide a list of available drives and then can dig in deeper, providing you with tons of details about any of the drives.
For an example of using the Drivelnfo class, create a simple Windows Form that will list out all the available drives on a computer and then will provide details on a user-selected drive Your Windows Form will consist of a simple ListBox and should look as illustratted.
Once you have the form all set, the code will consist of two events – one for when the form loads and another for when the end user makes a drive selection in the list box.
The code for this form is shown here:
public partial class Form1 : Form
public Form1 ()
‘private void Forml_Load(object sender, EventArgs e)
Drivelnfo di = Drivelnfo.GetDrives();
” foreach (Drivelnfo itemDrive in di)
private void listBoxl_SelectedlndexChanged(object sender, EventArgs e)
Drivelnfo di = new Drivelnfo(listBoxl.Selectedltem.ToString());
MessageBox.Show(‘Available Free Space: ‘
+ di.AvailableFreeSpace + ‘\n’ +
‘Drive FOIlllAt:’, + dLDriveFoIlllAt + ‘\n’ +
‘Drive Type: ‘ + di.DriveType + ‘\n’ +
‘Is Ready: ‘ + di.IsReady + ‘\n’ +
‘Name: ‘ + di.Name + ‘\n’ +
‘Root!’Directory’: ‘ + di.RootDirectory + ‘\n’ +
‘ToString() Value: ‘ + di + ‘\n’ +
‘Total Free Space: ‘ + di.TotalFreeSpace + ‘\n’ +
‘Total Size: ‘ + di.TotalSize.+ ‘\n’ +
‘Volume Label: ‘ + di.VolumeLabel. di.Name +
, DRIVE INFO’);
The first step is to bring in the System. IO namespace with the using keyword. Within the Forml_Load event, you use the DriveInfo class to get a list of all the available drives on the system.
This is done using an array of DriveInfo objects and populating this array with the DriveInfo.
GetDrives () method then using a for each loop, you are able to iterate through each drive found and populate the list box with the results.
This produces something similar.
This form allows the end user to select one of the drives in the list. Once a drive is selected, a message box appears that contains details about that drive.
I have six drives on my current computer Selecting a couple of these drives produces the message boxes.
From here, you can see that these message boxes provide details about three entirely different drives.
The first, drive C: \, is my hard drive, as the message box shows its drive type as Fixed.
The second drive, drive 0: \, is my CD/DVD drive.
The third drive, drive F: \, is my USB pen and is labeled with a drive type of Removable.