I/O in the .NET Framework Chapter One Streams
1.2.1 Streams for files
Start a new Visual Studio .NET Windows application project.
Drag an File Open Dialog control onto the form. Name this control openFileDialog. Then add a textbox, to be named tbResults, which should be set with multiline=true. Add two buttons to the form, and
name them btnReadAsync and btnReadSync.
First, we shall implement asynchronous file reading. Press Read Async
and enter the following code:
FileStream fs;
byte[] fileContents;
AsyncCallback callback;
private void btnReadAsync_Click(object sender,
System.EventArgs e)
{
openFileDialog.ShowDialog();
callback = new AsyncCallback(fs_StateChanged);
fs = new FileStream(openFileDialog.FileName, FileMode.Open,
FileAccess.Read, FileShare.Read, 4096, true);
fileContents = new Byte[fs.Length];
fs.BeginRead(fileContents, 0, (int)fs.Length, callback,
null);
}
This code requires a little explanation. First, the magic number, 4096, is simply a performance characteristic because it is quicker to transfer data from disks in 4-Kb chunks than 1 byte at a time.
The final parameter in the FileStream constructor indicates whether the operation is to be completed asynchronously or synchronously.
The most important thing to note is that there is no reference to tbResults; this implies that some other function must handle the data once the read is complete. The AsyncCallback constructor refers to another function, which is also referenced in the BeginRead method, so this must be it.
As you can see from the code, the fs_StateChanged function has not yet been implemented. This function is called whenever the file is finished reading.
Note: Synchronous use of FileStream is more efficient when the file size is less than 64 Kb and the file is located on the local machine.
private void fs_StateChanged(IAsyncResult asyncResult)
{
if (asyncResult.IsCompleted)
{
tbResults.Text = Encoding.UTF8.GetString(fileContents);
fs.Close();
}
}
private void btnReadSync_Click(object sender,
System.EventArgs e)
{
Thread thdSyncRead = new Thread(new ThreadStart(syncRead));
thdSyncRead.Start();
}
This code doesn’t perform any file handling; instead, it creates a new thread, whose entry point is the syncRead function. When this thread runs, it does so in parallel with any other code that is running at the same time, which includes the background operating system (OS) “housekeeping” (Windows message handling) functions.
If the code above were replaced by a simple call to syncRead(), the program would still operate; however, if the file happened to be several gigabytes in size, the user would quickly perceive the application to be “hung.” A hung application is notably nonresponsive and may turn white when dragged behind another application. What is actually happening is that the main thread of application is taking 100% processor time and does not give the OS time to handle simple tasks such as redrawing the user interface.
In certain time-critical applications, it may be necessary to take 100% processor time, but any application with a user interface should remain responsive at all times.
The next task is to implement the syncRead function:
public void syncRead()
{
openFileDialog.ShowDialog();
FileStream fs;
try
{
fs = new FileStream(ofd.FileName, FileMode.OpenOrCreate);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
fs.Seek(0, SeekOrigin.Begin);
byte[] fileContents = new byte[fs.Length];
fs.Read(fileContents, 0, (int)fs.Length);
tbResults.Text = Encoding.UTF8.GetString(fileContents);
fs.Close();
}
In the above code, you will notice that the FileStream constructor is enclosed in a try/catch block. This allows the program to recover gracefully from problems such as a missing file or an unreadable disk. In realworld applications, any operation that relies on the existence of files or network resources should be contained within a try/catch block. This allows programs to continue execution, even if something unexpected happens. In most examples throughout this book, try/catch blocks are not used in order to keep the examples concise and readable.
Three namespaces must be included in the code as follows:
using System.IO;
using System.Text;
using System.Threading
To test the application, press Debug → Start. Press either button, and then open a file, and you will see its contents in the textbox opposite. Many files, such as those designed to hold audio or video data, will display as pages of seemingly random characters because the data is not designed to be displayed as text and requires another program to
interpret into something we can see or hear.
An interesting observation you can make with this application is that if you compare the textual representation of a database file (.mdb) with an Mp3 (.mp3), you will notice that the database file contains many identical
pages of text, whereas the Mp3 file contains a real mixture of characters. The similarity of data within a file is known as its entropy. By reducing the entropy of data in a file, the file size can be reduced. This is why a database
shrinks in size when compressed, but an Mp3 doesn’t.
The significant methods and properties for FileStream are shown in:
Constructor - Initializes a new instance of the FileStream. It may be invoked thus: FileStream(string, FileMode).
Length - Gets the length of the file. Returns long.
Position - Gets or sets the current position of the file pointer. Returns long.
BeginRead() - Begins an asynchronous read. It may be invoked thus:
BeginRead(byte[] array,int offset,int numBytes, AsyncCallback userCallback, object stateObject).
BeginWrite() - Begins an asynchronous write. It may be invoked thus:
BeginWrite(byte[] array,int offset,int numBytes, AsyncCallback userCallback, object stateObject).
Write - Writes a block of bytes to this stream using data from a buffer. It may be invoked thus: Write(byte[] array,int
offset,int count).
Read - Reads a block of bytes from the stream and writes the data in a given buffer. It may be invoked thus: Read(in byte[] array,int offset, int count).
Lock - Prevents access by other processes to all or part of a file. It may be invoked thus: Lock (long position, long
length).