July 29, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

File I/O with Streams - Part 1 - An Excerpt from Inside C#, Second Edition

  • March 7, 2003
  • By Tom Archer
  • Send Email »
  • More Articles »

Binary Readers and Writers

Recall that the StreamWriter class provides a text-interpolation layer on top of another stream, such as a FileStream. The StreamWriter.Write method is heavily overloaded. Thus, not only can we pass it text, we also can pass it data of type char, int, float, or any other standard type.as well as an open-ended (params) number of objects and anything derived from those objects. Of course, this works similarly to the way that Console.Write works: all data of all types is converted to text before being written to the stream. The BinaryWriter class allows you to write data to a stream without this text interpolation so that the data is written in binary form. For example, compare the two files that result from the following code.one written by using StreamWriter, the other with BinaryWriter:
class BinReadWriteApp
{
  static void Main(string[] args)
  {
    Stream s = new FileStream("Foo.txt", FileMode.Create);
    StreamWriter w = new StreamWriter(s);
    w.Write("Hello World ");
    w.Write(123);
    w.Write(' ');
    w.Write(45.67);
    w.Close();
    s.Close();

    Stream t = new FileStream("Bar.dat", FileMode.Create);
    BinaryWriter b = new BinaryWriter(t);
    b.Write("Hello World ");
    b.Write(123);
    b.Write(' ');
    b.Write(45.67);
    b.Close();
    t.Close();
  }
}

You can open the two output files in Notepad or from the Microsoft Visual Studio File|Open|File menu. The contents of Foo.txt are shown here:

Hello World 123 45.67

We.d probably accept that the unreadable stuff after "Hello, World " is the binary numeric data. A quick check of the ASCII table reveals that the decimal value 123 is the character "{", so the rest must be the floating-point value. But what.s that unprintable character at the beginning of the file? If we open the file in Visual Studio instead of in Notepad, Visual Studio will use a binary/hex editor when it recognizes nonprintable characters in the file. Take a look at Figure 2.



Figure 2 - A binary file in the binary/hex editor. (click for larger image)

Now it becomes clearer. The string .Hello, World. is easy to see, followed by 0x20, which is the space; then the decimal 123, which is 0x7B (the character .{.); followed by another space; then 8 bytes of floating-point value. As you can see, the value of the very first byte in the stream is 0x0C, or decimal 12. Anyone with any experience using strings in Microsoft Visual Basic or using BSTRs in COM development will recognize this right away. It.s the length of the following string.including the space at the end.

Naturally, if you plan to write binary data, you.ll want to read back binary data by using the BinaryReader class, as illustrated in the following code. When you do so, remember to keep the Close calls at the end.

b.BaseStream.Position = 0;
BinaryReader r = new BinaryReader(t);
int i = 0;
while (true)
{
  i = b.BaseStream.ReadByte();
  if (-1 == i)
  {
    Console.WriteLine();
    break;
  }
  Console.Write("{0,-4}", i);
}

r.Close();
b.Close();
t.Close();

Here's the output from this revised application:

12  72  101 108 108 111 32  87  111 114 108 100 32  
123 0   0   0   32  246 40  92  143 194 213 70  64

File System Classes

In addition to the Stream class and its various derivatives, the .NET Framework classes also offer a set of file system.related classes for encapsulating information and functionality that's suitable for processing files and directories. These classes reside in the System.IO namespace and are listed in Table 2.

Table 2 - File system classes in the .NET Framework Classes

Class

Description

FileSystemInfo

The abstract base class for FileInfo and DirectoryInfo objects, this class contains methods that are common to both file and directory manipulation. Useful when processing a lot of files and directories.

DirectoryInfo

Supports creation, deletion, and manipulation of directories (only instance methods).

FileInfo

Supports creation, deletion, and manipulation of files (only instance methods).

Directory

Supports creation, deletion, and manipulation of directories. (All methods are static.)

File

Supports creation, deletion, and manipulation of files. (All methods are static.)

Path

Performs operations on a String that contains file or directory path information.the file or directory doesn't need to exist.

Directory and DirectoryInfo

The FileInfo and DirectoryInfo classes encapsulate information about files and directories as well as methods, such as Create, Delete, Open, MoveTo, and CopyTo. These classes two also offer behavior similar to that of CFile in the Microsoft Foundation Classes (MFC) and fstream in the Standard C++ library. The following example gets a DirectoryInfo object from the static Directory.GetCurrentDirectory method and then calls the DirectoryInfo.GetFiles instance method to get a collection of FileInfo objects. Finally, we iterate this collection to report on some arbitrary properties of each file:
public class DirInfoApp
{
  public static void Main(string[] args)
  {
    DirectoryInfo dir = 
      new DirectoryInfo(Directory.GetCurrentDirectory());
    Console.WriteLine("Current Dir: {0}", dir.FullName);

    foreach (FileInfo f in dir.GetFiles())
    {
      Console.WriteLine("{0,-14}{1,10}{2,20}",
                        f.Name, f.Length, 
                        f.LastWriteTime);
    }
  }
}

Here's the output:

Current Dir: C:\InsideCsharp\Chap11\DirInfo\DirInfo\bin\Debug
DirInfo.exe         6144 04/01/2002 21:06:30
DirInfo.pdb        13824 04/01/2002 21:06:30

In addition to using GetCurrentDirectory, we can construct DirectoryInfo objects by using a string for the desired path:

dir = new DirectoryInfo(".");
dir = new DirectoryInfo(@"C:\Winnt");

The first example, of course, will produce the same results as calling GetCurrentDirectory will. Another useful method is GetDirectories, which will return as DirectoryInfo objects a collection of subdirectories of the current directory. The common parentage of DirectoryInfo and FileInfo is clear from the following code:

Console.WriteLine("\n{0,-32}{1}", "Name", "LastWriteTime");
foreach (DirectoryInfo d in dir.GetDirectories())
{
  Console.WriteLine(
  "{0,-32}{1}", d.Name, d.LastWriteTime);
}

This is the output:

Name                            LastWriteTime
$NtUninstallQ301625$            24/12/2001 17:37:43
A5W_DATA                        11/02/2001 15:18:25
addins                          25/11/2001 12:16:25
AppPatch                        24/12/2001 15:14:34
Assembly                        24/12/2001 15:23:47
Config                          25/11/2001 12:17:06
Connection Wizard               07/01/2000 11:27:04
CSC                             25/11/2001 17:56:56
Cursors                         24/12/2001 15:15:07
Debug                           07/01/2002 09:00:23
Downloaded Program Files        16/12/2001 11:15:14
Driver Cache                    07/01/2000 11:27:04
Fonts                           25/11/2001 15:17:08
Help                            24/12/2001 16:03:31
IIS Temporary Compressed Files  06/04/2001 10:46:27
...

The DirectoryInfo class offers a reasonable set of methods for creating, deleting, moving, and so on. For instance, we could create a new directory at some arbitrary location. (In the following example, the new directory is created within the current directory.) We could then create a subdirectory within the new directory, set and then get some attributes, and finally delete both the subdirectory and the newly created parent directory:

dir = new DirectoryInfo("Foo");
if (false == dir.Exists)
  dir.Create();

DirectoryInfo dis = dir.CreateSubdirectory("Bar");
dis.Attributes |= FileAttributes.Hidden | FileAttributes.Archive;

Console.WriteLine("{0,-10}{1,-10}{2}", 
                  dis.Name, dis.Parent, dis.Attributes);

dis.Delete(true);
dir.Delete(true);

The output follows:

Bar       Foo       Hidden, Directory, Archive

Recall that the library also offers the Directory class, which exposes only static methods. Thus, in the previous code, we could've used the static method Directory.Delete instead and achieved the same results:

//dir.Delete(true);
Directory.Delete(dir.Name, true);

The set of methods offered by the Directory class more or less parallels the instance methods in the DirectoryInfo class. The same parallelism is true for the File and FileInfo classes. In both cases, a couple of additional methods that aren't offered by the parallel class exist. For instance, the Directory class offers a GetLogicalDrives method:

string[] sa = Directory.GetLogicalDrives();
Console.WriteLine("Logical Drives:");
foreach (string s in sa)
{
  Console.Write("{0,-4}", s);
}

Here's the output:

Logical Drives:
A:\ C:\ D:\ E:\ F:\




Page 5 of 6



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel