Overview of File Processing

Introduction

So far, if we requested information from the user, when the application exited, we lost all information that the user had entered. This is because such information was only temporarily stored in the random access memory (RAM).

In some cases, you will want to "keep" information that the user had entered so you can make the information available the next time the user opens the application. In some other cases, whether you request information from the user or inherently provide it to the user, you may want different people working from different computers to use or share the same data. In these and other scenarios, you must store the information somewhere and retrieve it when necessary. This is the basis of file processing.

Practical LearningPractical Learning: Introducing File Processing

  1. Start Microsoft Visual Studio and create a Windows Forms application named GasUtilityMeters1
  2. Right-click the form and click View Code

Files

A piece of information used in an application is primarily represented as a group of bits. A file is a series of bits or bytes of data that are arranged in a particular manner to produce a usable document. For easy storage, location, and management, the bytes are stored on a medium such as a hard drive, a solid state drive, or any valid and supported type of storage. When these bytes belong to a single but common entity and hold values that are stored on a medium, the group is referred to as a file.

For greater management, files can be stored in a parent object called a directory or a folder. Since a file is a unit of storage and it stores information, it has a size, which is the number of bits it uses to store its values. To manage it, a file has a location also called a path that specifies where and/or how the file can be retrieved. Also, for better management, a file has attributes (characteristics) that indicate what can be done on the file or that provide specific information that the programmer or the operating system can use when dealing with the file.

Streams

File processing consists of creating, storing, and/or retrieving the contents of a file from a recognizable medium. A stream is the technique or means of performing file processing. In order to manage files stored in a computer, each file must be able to provide basic pieces of information about itself. This basic information is specified when the file is created but can change during the lifetime of a file.

To create a file, a user must first decide where it would be located: this is a requirement. There are various means of creating files. When creating a file, the user must give it a name following the rules of the operating system combined with those of the file system. The most fundamental piece of information of a file must is a name.

Once the user has created a file, whether the file is empty or not, the operating system assigns basic pieces of information to it. Once a file is created, it can be opened, updated, modified, renamed, etc.

Streaming Prerequisites

Introduction

To support file processing, the .NET Framework provides the System.IO namespace that contains many different classes to handle almost any type of file operation you may need to perform. Therefore, to perform file processing, you can include the System.IO namespace in your project.

The parent class of file processing is named Stream. With the Stream class, you can store data to a stream or you can retrieve data from a stream. Stream is an abstract class, which means that you cannot use it to declare a variable in your application. As an abstract class, Stream is used as the parent of the classes that actually implement the necessary operations. You will usually use a combination of classes to perform a typical operation. For example, some classes are used to create a stream object while some others are used to write data to the created stream.

Practical LearningPractical Learning: Introducing the Input/Output Namespace

The Name of a File

Before performing file processing, one of your early decisions will consist of specifying the type of operation you want the user to perform. For example, the user may want to create a brand new file, open an existing file, or perform a routine operation on a file. In all or most cases, whether you are creating a new file or manipulating an existing one, you must specify the name of the file. You can do this by declaring a string variable but, as we will learn later on, most classes used to create a stream can take a string that represents the file.

If you are creating a new file, there are certainly some rules you must observe. The name of a file follows the directives of the operating system. To disting files, their name end with a period and one or more characters and/or symbols. This last character or combination is referred to as the file extension. The names of folders and files on Microsoft Windows >= 95 can have up to 255 characters. The extension of the file is mostly left to the judgment of the programmer. An application can be configured to save different types of files; that is, files with different extensions.

Based on this, if you declare a string variable to hold the name of the file, you can simply initialize the variable with the necessary name and its extension. Here is an example:

private void btnSave_Click(object sender, EventArgs e)
{
    string Filename = "Employees.spr";
}

The Path to a File

If you declare a string as above, the file will be created in the folder of the application. Otherwise, you can create your new file anywhere in the hard drive. To do that, you must provide a complete path where the file will reside. A path is a string that specifies the drive (such as A:, C:, or D:). The sections of a complete path string are separated by a backslash. For example, a path can be made of a folder followed by the name of the file. An example is:

C:\Palermo.tde 

A path can also consist of a drive followed by the name of the folder in which the file will be created. Here is an example:

C:\Program Files\Palermo.tde

A path can also indicate that the file will be created in a folder that itself is inside of another folder. In this case, remember that the names of folder must be separated by backslashes.

The backslash character is used to create or manage escape sequences and it can be included in a string value to make up an escape sequence. Because of this, every time you include a backslash in a string, the compiler thinks that you are trying to provide an escape sequence. In this case, if the combination of the backslash and the character that follows the backslash is not recognized as an escape sequence, you would get an error. To solve this problem, you have two options. To indicate that the backslash must be considered as a character in its own right, you can double it. Here are examples:

using System;

class Exercise
{
    static int Main(string[] args)
    {
		string Filename = "C:\\Documents and Settings\\Employees.spr";

		return 0;
    }
}

Alternative, you can keep one backslash in each placeholder but precede the value of the string with the @ symbol. Here is an example:

using System;

class Exercise
{
    static int Main(string[] args)
    {
		string Filename = @"C:\Documents and Settings\Employees.spr";

		return 0;
    }
}

In the same way, you can declare a string variable to represent the name of an existing file that you plan to use in your program. You can also represent its path.

When providing a path to the file, if the drive you specify doesn't exist or cannot be read, the compiler would consider that the file doesn't exist. If you provide folders that don't exist in the drive, the compiler would consider that the file doesn't exist. This also means that the compiler will not create the folder(s) (the .NET Framework provides all means to create a folder but you must ask the compiler to create it; simply specifying a folder that doesn't exist will not automatically create it, even if you are creating a new file). Therefore, it is your responsibility to make sure that either the file or the path to the file is valid. As we will see in the next sections, the compiler can check the existence of a file or path.

Introduction to Drives

Description

A drive is a physical device attached to a computer so it can store information. A drive can be a hard drive, a digital drive (CD ROM, DVD ROM), a flash (USB) drive, a memory card etc:

Hard Drive DVD Drive USB Flash Drive
Hard Drive DVD Drive USB Flash Drive
      
Floppy Drive Memory Card
Floppy Drive Flash Memory

A drive can reside inside a computer. That's the case for internal hard drives and most CD or DVD drives. A drive can also reside outside. That's the case for most flash (USB) drives. There are also versions of external hard drives and external DVD drives:

Hard Drive Floppy Drive USB Flash Drive
External Hard Drive External Floppy Drive USB Flash Drive Holder

A drive is referred to as virtual if it is not a real physical object.

While most drives are connected to a computer, a device connected to another computer can also be used as a drive. This is the case for network drives. The drive most be "shared" so that networked computers can access it.

Not all computers have the same drives and not all computers deal with the same means of storing data. Still, to simplify their identification, all objects used to hold data are referred to as drives. Because there are different ways to consider drives, there are also various means of accessing them.

There are two techniques of referring to drives. A drive that is directly connected to a computer, whether internally or externally, is referred to as a local drive. In Microsoft Windows, a local drive is represented by a letter, in uppercase, followed by a colon ":", and a backslash "\". If a hard disk is partitioned, each partition uses its own letter and is represented as its own drive.

Getting the List of Drives of a Computer

Normally, you will hardly be concerned with the creation of drives. The operating system "creates" or assigns a drive whenever it juges it necessary. For example, as soon as you connect a USB drive to a port, the operating system automatically creates a drive and assigns a lette to it. You will only need to identify the drives that are available on a computer on which  your application is running. One of the primary operations you will perform consists of getting a list of drives on the computer.

The .NET Framework provides many classes that can assist you to identify the drives of a computer. The main class used to manage the drives of a computer is named DriveInfo.

We were already introduced to a class named Environment. This class is equipped with a method named GetLogicalDrives. Its syntax is:

public static string[] GetLogicalDrives()

When called, this method produces an array of strings where each element represents a logical drive in the computer. You can then use the drives as you see fit. Here is an example:

private void btnDrives_Click(object sender, EventArgs e)
{
    string[] strDrives = Environment.GetLogicalDrives();

    foreach (string strDrive in strDrives)
        MessageBox.Show("Logical Drive: " + strDrive,
                        "Logical Drives",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
}

Besides Environment, the .NET Framework provides a class named DriveInfo. Besides the Environment.GetLogicalDrives() method, the DriveInfo class is equipped with a method named GetDrives().Its syntax is:

public static DriveInfo[] GetDrives();

This methodd provides its own means of getting a list of logical drives on the local computer. As opposed to a list of strings, the DriveInfo.GetDrives() method produces an array of DriveInfo objects, which is an array of logical drives on the computer. Here is an example of calling this method:

private void btnDrives_Click(object sender, EventArgs e)
{
    DriveInfo[] diLocalDrives = DriveInfo.GetDrives();
}

Characteristics of Drives

Introduction

In microsoft Windows, a local drive is represented by a letter followed by :\. For example, in most personal computers, the (main) hard drive is represented as C:\.

If you know the drive you want to use or access, the DriveInfo class provides a constructor that allows you to get a reference to that drive. This is the only constructor of this class. The syntax of the constructor is:

public DriveInfo(string driveName);

If you want to access a drive, you can declare a DriveInfo variable and pass the drive letter to this constructor. Here is an example:

using System;
using System.IO;
using System.Windows.Forms;

namespace DrivesCharacteristics
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void btnDrives_Click(object sender, EventArgs e)
        {
            DriveInfo diHardDrive = new DriveInfo(@"C:\");
        }
    }
}

The Name of a Drive

If you want to get the name of a drive, you can access the Name property of the DriveInfo class:

public string Name { get; }

Here is an example:

private void btnDrives_Click(object sender, EventArgs e)
{
    DriveInfo[] diLocalDrives = DriveInfo.GetDrives();

    foreach (DriveInfo diLogicalDrive in diLocalDrives)
        MessageBox.Show("Logical Drive: " + diLogicalDrive.Name,
                        "Logical Drives",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
}

The Type of a Drive

A drive is primarily recognized by its category. Examples of categories are hard drives, optical drives (CD, DVD, BluRay drives), etc. The categories of drives are stored in the DriveType property of the DriveInfo class. The DriveType property is based on an enumeration of the same name:

public DriveType DriveType { get; }

Its members are:

Member Description
Unknown The drive is unrecognizable
NoRootDirectory The root of the drive is unrecognizable
Removable This can be a floppy drive, a USB drive, a memory card, etc. A drive that can be removed at will
Fixed This is a hard drive or a partition on an HD
Network This is a network drive, usually located on another computer
CDROM This is drive CD or DVD drive
Ram This is the random access memory

The Type of Format

The format system is the scheme that a computer (actually the operating system) uses to store and process the values in its drives. Microsoft Windows uses various types of formats, including FAT32 and NTFS. To know the format scheme that a drive is using, get the value of the DriveFormat property of the DriveInfo class:

public string DriveFormat { get; }

Here is an example of accessing this property:

private void btnDrives_Click(object sender, EventArgs e)
{
    DriveInfo diHardDrive = new DriveInfo(@"C:\");

    if( diHardDrive != null  )
        MessageBox.Show("Operating System:  " + 
                        Environment.OSVersion.ToString() + Environment.NewLine +
                        "Drive Format:      " + diHardDrive.DriveFormat,
                        "Exercise",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
}

The Capacity of a Drive

A drive is priarily used to hold some values. The capacity of a drive is the amount of data it can hold. This is usually measured in bits and bytes. As there are various types of drives, they also have different capacities. To help you to know the capacity of a drive, the DriveInfo class is equipped with a property named TotalSize that is of type long:

public long TotalSize { get; }

This property produces a long integer that represents the normal total capacity of a drive. Here is an example of accessing it:

private void btnDrives_Click(object sender, EventArgs e)
{
    DriveInfo diHardDrive = new DriveInfo(@"C:\");

    if( diHardDrive != null  )
        MessageBox.Show("You hard drive has a capacity of " +
                        diHardDrive.TotalSize.ToString("N") + " bytes.",
                        "Logical Drives",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
}

As values are stored in a drive, the drive gets filled up. In some drives, values can be deleted, copied, or moved. This means that the capacity of some drives changes some time to time. At one time, to know the available free space of a drive, you can get the value of the AvailableFreeSpace property of its DriveInfo object:

public long AvailableFreeSpace { get; }

Here is an example of accessing this property:

private void btnDrives_Click(object sender, EventArgs e)
{
    DriveInfo diHardDrive = new DriveInfo(@"C:\");

    if( diHardDrive != null  )
        MessageBox.Show("Hard Drive Capacity:  " +
                        diHardDrive.TotalSize.ToString() + " bytes.\n" +
                        "Available Free Space: " +
                        diHardDrive.AvailableFreeSpace.ToString() + " bytes.\n",
                        "Exercise",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
}

Directories

Introduction

A directory is a section of a medium (floppy disc, flash drive, hard drive, CD, DVD, BD, etc) used to delimit a group of files. Because it is a "physical" area, it can handle operations not available on files. In fact, there are many fundamental differences between both:

The similarities of both types are:

Directory Creation

Before using a directory, you must first have it. You can use an existing directory if the operating system or someone else had already created one. You can also create a new directory. Directories are created and managed by various classes but the .NET Framework provides a class named Directory. This is the fundamental class to deal with directories. Directory is a static class. All of its methods are static, which means you will never need to declare an instance of the Directory class in order to use it.

Besides the Directory class, the .NET Framework provides a class named DirectoryInfo. This call supports additional operations on folders and sub-folders.

To create a directory, you can call the CreateDirectory() method of the Directory class. This method is available in two versions. One of the versions uses the following syntax:

public static DirectoryInfo CreateDirectry(string path);

This method takes as argument the (complete) path of the desired directory. Here is an example:

E:\Programs\Business Orders\Customer Information

When this method is called:

  1. It first checks the parent drive, in this case E.
    If the drive doesn't exist, because this method cannot create a drive, the compiler would throw a DirectoryNotFoundException exception
  2. If the drive (in this case E) exists, the compiler moves to the first directory part of the path; in this case this would be the Programs folder in the E drive.
    If the folder doesn't exist, the compiler would create it. If that first directory doesn't exist, this means that the other directory(ies), if any, under the first don't exist. So, the compiler would create it/them
  3. If the first directory exists and if there is no other directory under that directory, the compiler would stop and would not do anything further
  4. If the directory exists and there is a sub-directory specified under it, the compiler would check the existence of that directory.
    If the sub-directory exists, the compiler would not do anything further and would stop.
    If the sub-directory doesn't exist, the compiler would create it
  5. The compiler would repeat step d until the end of the specified path

The Directory.CreateDirectory() method returns a DirectoryInfo object that you can use as you see fit.

Practical LearningPractical Learning: Introducing the Input/Output Namespace

  1. Change the document as follows:
    using System;
    using System.IO;
    using System.Windows.Forms;
    
    namespace GasUtilityMeters1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                /* If you want to use a drive other than the C:> drive, 
                 * change the letter in the following line. */
                Directory.CreateDirectory(@"C:\Gas Utility Company");
            }
        }
    }
  2. To execute the project and create the directory for the eventual files, on the main menu, click Debug -> Start Without Debugging
  3. Open a file utility such as Windows Explorer and check that a folder named Gas Utility Company was created in the designated drive
  4. Close the form and return to your programming environment

Checking for a Directory Existence

Before using or creating a directory, you can first check if it exists. This is because, if a directory already exists in the location where you want to create it, you would be prevented from creating one with the same name. In the same way, if you just decide to directly use a directory that doesn't exist, the operation you want to perform may fail because the directory would not be found.

To check whether a directory exists or not, you can call the Directory.Exists() Boolean static method. Its syntax is:

public static bool Exists(String path);

This method receives the (complete) path of the directory. If the path exists, the method returns true. If the directory doesn't exist, the method returns false.

Deleting a Directory

To get rid of a directory, you can call the Delete() method of the Directory class. It is overloaded with two versions. One of the versions uses the following syntax;

public static void Delete(string path);

When calling this method, pass the complete path as argument. The other version uses the following syntax:

public static void Delete(string path, bool recursive);

This time, the second argument allows you to specifies whether you want the sub-folders and their contents to be deleted also.

Listing the Files of a Directory

One of the most routine operations performed in a directory consists of looking for a file. Microsoft Windows operating systems and the user's intuition have different ways of addressing this issue. The .NET Framework also provides its own means of performing this operation, through various techniques. You can start by checking the sub-directories and files inside of a main directory.

To look for files in a directory, the DirectoryInfo class can assist you with its GetFiles() method, which is overloaded with three versions.

The .NET Framework Support for Files

Introduction

The primary support of a file as an object is provided by a .NET Framework class called File. This static class is equipped with various types of (static) methods to create, save, open, copy, move, delete, or check the existence of a file.

Practical LearningPractical Learning: Introducing Files

  1. On the main menu, click Window -> Form1.cs [Design]
  2. Design the form as follows:

    Introducing the Calendar Control

    Control (Name) Text
    GroupBox gbxGasMeter New Gas Meter
    Label   Meter #:
    TextBox txtMeterNumber  
    Label   Make:
    TextBox txtMake  
    Label   Model:
    TextBox txtModel  
    Label   Meter Reading Date:
    MonthCalendar mcMeterReadingDate  
    Label   Counter Value:
    TextBox txtCounterValue  
    Button btnSave Save

File Existence

One of the valuable operations that the File class can perform is to check the existence of the file you want to use. For example, if you are creating a new file, you may want to make sure it doesn't exist already because if you try to create a file that exists already, the compiler may first delete the old file before creating the new one. This could lead to unpredictable results, especially because such a file is not sent to the Recycle Bin. On the other hand, if you are trying to open a file, you should first make sure the file exists, otherwise the compiler will not be able to open a file it cannot find.

To check the existence of a file, the File class is equipped with a method named Exists. Its syntax is:

public static bool Exists(string path);

If you provide only the name of the file, the compiler would check it in the folder of the application. If you provide the path to the file, the compiler would check its drive, its folder(s) and the file itself. In both cases, if the file exists, the method returns true. If the compiler cannot find the file, the method returns false. It's important to know that if you provided a complete path to the file, any slight mistake would produce a false result.

File Creation

Besides checking the existence of the file, the File class can be used to create a new file. To support this operation, the File class is equipped with a method named Create. This meathod is overloaded with two versions as follows:

public static FileStream Create(string path);
public static FileStream Create(string path, int buffersize);

In both cases, the File.Create() method returns a Stream value, in this case an object of a class named FileStream. As the File.Create() method indicates, it takes the name or path of the file as argument. If you know or want to specify the size, in bytes, of the file, you can use the second version.

To provide the same operation of creating a file, you can use the Open() method of the File class. It is overloaded in three versions as follows:

public static FileStream Open(string path, FileMode mode);
public static FileStream Open(string path, 
		                      FileMode mode, 
		                      FileAccess access);
public static FileStream Open(string  path,
                          	  FileMode mode,
       		                  FileAccess access,
   		                      FileShare share);

Access to a File

In order to perform an operation on a file, you must specify to the operating system how to proceed. One of the options you have is to indicate the type of access that will be granted on the file. This access is specified using an enumeration named FileAccess. The members of the FileAccess enumeration are:

File Sharing

In standalone workstations, one person is usually able to access and open a file then perform the necessary operations on it. In networked computers, you may create a file that different people can access at the same time or you may make one file access another file to retrieve information. One file may depend on another. Based on this, when an operation must be performed on a file, you may have to specify how a file can be shared. This is done through an enumeration named FileShare.

The values of the FileShare enumeration are:

The Mode of a File

Besides the access to a file, another option you will most likely specify to the operating system is referred to as the mode of a file. It is specified through an enumeration named FileMode. The members of the FileMode enumeration are:

Fundamentals of File Streaming

Introduction

File streaming consists of performing one of the routine operations on a file, such as creating or opening it. This basic operation can be performed using a class called FileStream. You can use a FileStream object to get a stream ready for processing. As one of the most complete classes of file processing of the .NET Framework, FileStream is equipped with all necessary properties and methods. To use it, you must first declare a variable of it. The class is equipped with nine constructors.

One of the constructors (the second) of the FileStream class has the following syntax:

public FileStream(string path, FileMode mode);

This constructor takes as its first argument the name of the file or its path. The second argument specifies the type of operation to perform on the file. Here is an example:

using System;
using System.IO;
using System.Windows.Forms;

namespace FileProcessing1
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            string Filename = "Employees.spr";

            FileStream fstPersons = new FileStream(Filename,
                                                   FileMode.Create);
        }
    }
}

Stream Writing

A streaming operation is typically used to create a stream. Once the stream is ready, you can write data to it. The writing operation is performed through various classes. One of these classes is named BinaryWriter.

The BinaryWriter class can be used to write values of primitive data types (char, int, float, double, etc). To use a BinaryWriter value, you can first declare its variable. To do this, you would use one of the class' three constructors. The first constructor is the default, meaning it doesn't take an argument. The second constructor has the following syntax:

public BinaryWriter(Stream output);

This constructor takes as argument a Stream value, which could be a FileStream variable. Here is an example:

namespace FileProcessing1
{
    public partial class Exercise : Form
    {
        public Exercise()
        {
            InitializeComponent();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            string Filename = "Employees.spr";

            FileStream fstPersons = new FileStream(Filename,
                                                   FileMode.Create);
            BinaryWriter wrtPersons = new BinaryWriter(fstPersons);
        }
    }
}

Most classes that are used to add values to a stream are equipped with a method called Write. This is also the case for the BinaryWriter class. This method takes as argument the value that must be written to the stream. The method is overloaded so that there is a version for each primitive data type. Here is an example that adds strings to a newly created file:

Persons

private void btnSave_Click(object sender, EventArgs e)
{
    string Filename = "Employees.spr";

    FileStream fstPersons = new FileStream(Filename,
                                           FileMode.Create);
    BinaryWriter wrtPersons = new BinaryWriter(fstPersons);
    
    wrtPersons.Write(txtPerson1.Text);
    wrtPersons.Write(txtPerson2.Text);
    wrtPersons.Write(txtPerson3.Text);
    wrtPersons.Write(txtPerson4.Text);

    txtPerson1.Text = "";
    txtPerson2.Text = "";
    txtPerson3.Text = "";
    txtPerson4.Text = "";
}

Stream Closing

When you use a stream, it requests resources from the operating system and uses them while the stream is available. When you are not using the stream anymore, you should free the resources and make them available again to the operating system so that other services can use them. This is done by closing the stream.

To let you close a stream, the classes used for streaming (reading or writing) are equipped with a method named Close. Here are examples of calling this method:

private void btnSave_Click(object sender, EventArgs e)
{
    string Filename = "Employees.spr";

    FileStream fstPersons = new FileStream(Filename,
                FileMode.Create);
    BinaryWriter wrtPersons = new BinaryWriter(fstPersons);
    
    wrtPersons.Write(txtPerson1.Text);
    wrtPersons.Write(txtPerson2.Text);
    wrtPersons.Write(txtPerson3.Text);
    wrtPersons.Write(txtPerson4.Text);

    txtPerson1.Text = "";
    txtPerson2.Text = "";
    txtPerson3.Text = "";
    txtPerson4.Text = "";

    wrtPersons.Close();
    fstPersons.Close();
}

Practical LearningPractical Learning: Writing to a Stream

  1. On the form, double-click the Save button
  2. Implement the Click event as follows:
    using System;
    using System.IO;
    using System.Windows.Forms;
    
    namespace GasUtilityMeters1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                /* If you want to use a drive other than the C:> drive, 
                 * change the letter in the following line. */
                Directory.CreateDirectory(@"C:\Gas Utility Company");
            }
    
            private void btnSave_Click(object sender, EventArgs e)
            {
                if (string.IsNullOrEmpty(txtMeterNumber.Text))
                {
                    MessageBox.Show("You must type a (unique) number for the gas meter.", "Gas Utility Company");
    
                    // If the user doesn't provide a meter number, don't do nothing.
                    return;
                }
    
                if (string.IsNullOrEmpty(txtMake.Text))
                {
                    MessageBox.Show("Please enter the name of the gas meter manufaturer.", "Gas Utility Company");
    
                    // If the user doesn't provide the meter manufaturer, don't do nothing.
                    return;
                }
    
                if (string.IsNullOrEmpty(txtModel.Text))
                {
                    MessageBox.Show("Can you identify the model of the gas meter? Type it.", "Gas Utility Company");
    
                    // If the user doesn't provide the meter model, don't do nothing.
                    return;
                }
    
                if (string.IsNullOrEmpty(txtCounterValue.Text))
                    MessageBox.Show("Type the initial or current reading counter on the gas meter.", "Gas Utility Company");
    
                /* We already have a directory where we will save the files of this application:
                 * Company File Repository: C:\Gas Utility Company */
                /* Each gas meter will have its own file. The name of the file of
                 * a gas meter will be the meter number and the "gms" extension. */
    
                /* Create a file stream that can be used to create a new file, 
                 * that can access a file to write to it, and that can allow 
                 * clients (such as other/networked computers to write to it. */
                FileStream fsGasMeters = new FileStream(@"C:\Gas Utility Company\" + txtMeterNumber.Text + ".gms", 
                                                        FileMode.Create, FileAccess.Write, FileShare.Write);
                // Create file writer that will be used to write values to the file stream
                BinaryWriter bwGasMeter = new BinaryWriter(fsGasMeters);
                
                // Get each value from the form and write it to the file
                bwGasMeter.Write(txtMeterNumber.Text);
                bwGasMeter.Write(txtMake.Text);
                bwGasMeter.Write(txtModel.Text);
                bwGasMeter.Write(mcMeterReadingDate.SelectionStart.ToShortDateString());
                bwGasMeter.Write(txtCounterValue.Text);
    
                // After using the writer, close it
                bwGasMeter.Close();
                // After using the file stream, close it
                fsGasMeters.Close();
    
                MessageBox.Show("A record for the gas meter has been created.", "Gas Utility Company");
    
                // After saving the file, reset the form by setting the initial/empty values to the controls
                txtMake.Text = "";
                txtModel.Text = "";
                txtMeterNumber.Text = "";
                mcMeterReadingDate.SelectionStart = DateTime.Today;
                txtCounterValue.Text = "0";
            }
        }
    }
  3. To execute, on the main menu, click Debug and click Start Without Debugging

    Clarksville Ice Cream

  4. Type or select the following values:
    Meter #:            582741-38
    Make:	              Sinton International
    Model:	          D-244
    Meter Reading Date: 5/10/2021
    Counter Value:	  138

    Writing to a Stream

  5. Click the Save button

    Writing to a Stream

  6. Click OK on the message box
  7. Create other files with the following values:
    Meter # Make Model Meter Reading Date Counter Value
    293847-27 Archimeda LP2066 8/3/2021 2866
    928731-59 EnvioSmart 84-D9703 12/8/2020 8016
  8. Close the form and return to your programming environment
  9. In Microsoft Visual Studio, ceate a new Windows Forms application named GasUtilityMeters2
  10. Design the form as follows:

    Introducing the Calendar Control

    Control (Name) Text
    GroupBox gbxGasMeter Gas Meter Editor
    Label   Meter #:
    TextBox txtMeterNumber  
    Button btnOpen Open
    Label   Make:
    TextBox txtMake  
    Label   Model:
    TextBox txtModel  
    Label   Meter Reading Date:
    MonthCalendar mcMeterReadingDate  
    Label   Counter Value:
    TextBox txtCounterValue  

Stream Reading

As opposed to writing to a stream, you may want to read existing data from it. Before doing this, you can first specify your intent to the streaming class using the FileMode enumeration. This can be done using the FileStream class as follows:

private void btnOpen_Click(object sender, EventArgs e)
{
    string Filename = "Employees.spr";
    FileStream fstPersons = new FileStream(Filename, FileMode.Open);
}

Once the stream is ready, you can get prepared to read data from it. To support this, the .NET Framework provides a class named BinaryReader. This class provides two constructors. One of the constructors (the first) has the following syntax:

public BinaryReader(Stream input);

This constructor takes as argument a Stream value, which could be a FileStream object. After declaring a FileStream variable using this constructor, you can read data from it. To support this, the class provides an appropriate method for each primitive data type.

After using the stream, you should close it to reclaim the resources it was using. As mentioned previously, this is done by calling the Close() method of the streaming class.

Here is an example of using the mentioned methods:

private void btnOpen_Click(object sender, EventArgs e)
{
    string Filename = "Employees.spr";
    FileStream fstPersons = new FileStream(Filename, FileMode.Open);
    BinaryReader rdrPersons = new BinaryReader(fstPersons);

    txtPerson1.Text = rdrPersons.ReadString();
    txtPerson2.Text = rdrPersons.ReadString();
    txtPerson3.Text = rdrPersons.ReadString();
    txtPerson4.Text = rdrPersons.ReadString();

    rdrPersons.Close();
    fstPersons.Close();
}

Practical LearningPractical Learning: Reading From a Stream

  1. On the form, double-click the Open button
  2. Change the document as follows:
    using System;
    using System.IO;
    using System.Windows.Forms;
    
    namespace GasUtilityMeters2
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnOpen_Click(object sender, EventArgs e)
            {
                FileStream fsGasMeters = new FileStream(@"C:\Gas Utility Company\" + txtMeterNumber.Text + ".gms", FileMode.Open, FileAccess.Read, FileShare.Read);
                BinaryReader brGasMeter = new BinaryReader(fsGasMeters);
                
                txtMeterNumber.Text = brGasMeter.ReadString();
                txtMake.Text = brGasMeter.ReadString();
                txtModel.Text = brGasMeter.ReadString();
                mcMeterReadingDate.SelectionStart = DateTime.Parse(brGasMeter.ReadString());
                txtCounterValue.Text = brGasMeter.ReadString();
                
                brGasMeter.Close();
                fsGasMeters.Close();
            }
        }
    }
  3. To execute, on main menu, click Debug -> Start Without Debugging

    Modeling a House

  4. Click the Meter # text box and type 293847-27

    Modeling a House

  5. Click the Open button:

    Modeling a House

  6. Close the form and return to Microsoft Visual Studio
  7. Close Microsoft Visual Studio

Previous Copyright © 2008-2021, FunctionX Next