Chapter 21. Streams

For many applications, data is held in memory and accessed as if it were a three-dimensional solid; when you need to access a variable or an object, you use its name—and, presto, it is available to you. When you want to move your data into or out of a file, or across the network or over the Internet, however, your data must be streamed. In a stream packets of data flow one after the other, much like bubbles in a stream of water.

The endpoint of a stream is a backing store. The backing store provides a source for the stream, like a lake provides a source for a river. Typically, the backing store will be a file, but it is also possible for the backing store to be a network or web connection.

Files and directories are abstracted by classes in the .NET Framework. These classes provide methods and properties for creating, naming, manipulating, and deleting files and directories on your disk.

The .NET Framework provides both buffered and unbuffered streams, and provides classes for asynchronous I/O as well. With asynchronous I/O you can instruct the .NET classes to read your file, and while they are busy getting the bits off the disk your program can be working on other tasks. The asynchronous I/O tasks notify you when their work is done. The asynchronous classes are sufficiently powerful and robust that you might be able to avoid creating threads explicitly (see Chapter 20).

Streaming into and out of files is no different than streaming across the network, and the second part of this chapter will describe streaming using both TCP/IP and web protocols.

To create a stream of data, your object must be serialized , or written to the stream as a series of bits. You have already encountered serialization in Chapter 19 . The .NET Frameworks provide extensive support for serialization, and the final part of this chapter will walk you through the details of taking control of the serialization of your object.

Files and Directories

Before looking at how you can get data into and out of files, let’s start by examining the support provided for file and directory manipulation.

The classes you need are in the System.IO namespace. These include the File class, which represents a file on disk, and the Directory class, which represents a directory (known in Windows as a folder ).

Working with Directories

The Directory class exposes static methods for creating, moving, and exploring directories. All the methods of the Directory class are static, and therefore you can call them all without having an instance of the class.

The DirectoryInfo class is a similar class but one which has nothing but instance members (i.e., no static members at all). DirectoryInfo derives from FileSystemInfo, which in turn derives from MarshalByRefObject. The FileSystemInfo class has a number of properties and methods which provide information about a file or directory.

Table 21-1 lists the principal methods of the Directory class, and Table 21-2 lists the principal methods of the DirectoryInfo class including important properties and methods inherited from FileSystemInfo.

Table 21-1. Principal methods of the Directory class

Method

Use

CreateDirectory( )

Creates all directories and subdirectories specified by its path parameter.

Delete( )

Deletes the directory and deletes all its contents.

Exists( )

Returns a Boolean value, true if the path provided as a parameter leads to an existing directory.

GetCreationTime( ) SetCreationTime( )

Returns the creation date and time of the directory; sets the creation date and time.

GetCurrentDirectory( ) SetCurrentDirectory( )

Returns the current directory; sets the current directory.

GetDirectories( )

Gets an array of subdirectories.

GetDirectoryRoot( )

Returns the root of the specified path.

GetFiles( )

Returns an array of strings with the filenames for the files in the specified directory.

GetLastAccessTime( ) SetLastAccessTime( )

Returns the last time the specified directory was accessed; sets the last time the specified directory was accessed.

GetLastWriteTime( ) SetLastWriteTime( )

Returns the last time the specified directory was written to; sets the last time the specified directory was written to.

GetLogicalDrives( )

Returns the names of all the logical drives in the form <drivel>:.

GetParent( )

Returns the parent directory for the specified path.

Move( )

Moves a directory and its contents to a specified path.

Table 21-2. Principal methods and properties of the DirectoryInfo class

Method or Property

Use

Attributes

Inherits from FileSystemInfo; gets or sets the attributes of the current file.

CreationTime

Inherits from FileSystemInfo; gets or sets the creation time of the current file.

Exists

Public property Boolean value, true if the directory exists.

Extension

Public property inherited from FileSystemInfo; the file extension.

FullName

Public property inherited from FileSystemInfo; the full path of the file or directory.

LastAccessTime

Public property inherited from FileSystemInfo; gets or sets the last access time.

LastWriteTime

Public property inherited from FileSystemInfo; gets or sets the time when the current file or directory was last written to.

Name

Public property name of this instance of DirectoryInfo.

Parent

Public property parent directory of the specified directory.

Root

Public property root portion of the path.

Create( )

Public method that creates a directory.

CreateSubdirectory( )

Public method that creates a subdirectory on the specified path.

Delete( )

Public method that deletes a DirectoryInfo and its contents from the path.

GetDirectories( )

Public method that returns a DirectoryInfo array with subdirectories.

GetFiles( )

Public method that returns a list of files in the directory.

GetFileSystemInfos( )

Public method that retrieves an array of FileSystemInfo objects.

MoveTo( )

Public method that moves a DirectoryInfo and its contents to a new path.

Refresh( )

Public method inherited from FileSystemInfo; refreshes the state of the object.

Creating a DirectoryInfo Object

To explore a directory hierarchy, you need to instantiate a DirectoryInfo object. The DirectoryInfo class provides methods for getting not just the names of contained files and directories, but also FileInfo and DirectoryInfo objects, allowing you to dive into the hierarchical structure, extracting subdirectories and exploring these recursively.

You instantiate a DirectoryInfo object with the name of the directory you want to explore:

DirectoryInfo dir = new DirectoryInfo(@"C:winNT");

Tip

Remember that the @ sign before a string creates a verbatim string literal in which it is not necessary to escape characters such as the backslash. This is covered in Chapter 10.

You can ask that DirectoryInfo object for information about itself, including its name, full path, attributes, the time it was last accessed, and so forth. To explore the subdirectory hierarchy, you ask the current directory for its list of subdirectories.

DirectoryInfo[] directories = dir.GetDirectories(  );

This returns an array of DirectoryInfo objects, each of which represents a directory. You can then recurse into the same method, passing in each DirectoryInfo object in turn:

foreach (DirectoryInfo newDir in directories)
{
   dirCounter++;
   ExploreDirectory(newDir);
}

The dirCounter static int member variable keeps track of how many subdirectories have been found altogether. To make the display more interesting, you’ll add a second static int member variable indentLevel that will be incremented each time you recurse into a subdirectory, and decremented when you pop out. This will allow you to display the subdirectories indented under the parent directories. The complete listing is shown in Example 21-1.

Example 21-1. Recursing through subdirectories

namespace Programming_CSharp
{
   using System;
   using System.IO;

   class Tester
   {
      public static void Main(  )
      {
         Tester t = new Tester(  );

         // choose the initial subdirectory
         string theDirectory = @"c:WinNT";

         // call the method to explore the directory,
         // displaying its access date and all
         // subdirectories
         DirectoryInfo dir = new DirectoryInfo(theDirectory);

         t.ExploreDirectory(dir);

         // completed. print the statistics
         Console.WriteLine(
            "

{0} directories found.
",
            dirCounter);
      }
        
      // Set it running with a directoryInfo object
      // for each directory it finds, it will call 
      // itself recursively
      private void ExploreDirectory(DirectoryInfo dir)
      {
         indentLevel++;  // push a directory level

         // create indentation for subdirectories
         for (int i = 0; i < indentLevel; i++)
            Console.Write("  "); // two spaces per level

         // print the directory and the time last accessed
         Console.WriteLine("[{0}] {1} [{2}]
", 
            indentLevel, dir.Name, dir.LastAccessTime);

         // get all the directories in the current directory
         // and call this method recursively on each
         DirectoryInfo[] directories = dir.GetDirectories(  );
         foreach (DirectoryInfo newDir in directories)
         {
            dirCounter++;  // increment the counter
            ExploreDirectory(newDir);
         }
         indentLevel--; // pop a directory level
      }

      // static member variables to keep track of totals
      // and indentation level
      static int dirCounter = 1;
      static int indentLevel  = -1; // so first push = 0
   }
}

Output (excerpt):
    [2] logiscan [5/1/2001 3:06:41 PM]

    [2] miitwain [5/1/2001 3:06:41 PM]

  [1] Web [5/1/2001 3:06:41 PM]

    [2] printers [5/1/2001 3:06:41 PM]

      [3] images [5/1/2001 3:06:41 PM]

    [2] Wallpaper [5/1/2001 3:06:41 PM]

363 directories found.

The program begins by identifying a directory (WinNT ) and creating a DirectoryInfo object for that directory. It then calls ExploreDirectory, passing in that DirectoryInfo object. ExploreDirectory displays information about the directory, and then retrieves all the subdirectories.

The list of all the subdirectories of the current directory is obtained by calling GetDirectories. This returns an array of DirectoryInfo objects. ExploreDirectory is the recursive method; each DirectoryInfo object is passed into ExploreDirectory in turn. The effect is to push recursively into each subdirectory, and then pop back out to explore sister directories until all the subdirectories of WinNT are displayed. When ExploreDirectory finally returns, the calling method prints a summary.

Working with Files

The DirectoryInfo object can also return a collection of all the files in each subdirectory found. The GetFiles( ) method returns an array of FileInfo objects, each of which describes a file in that directory. The FileInfo and File objects relate to one another, much as DirectoryInfo and Directory do. Like the methods of Directory, all the File methods are static; and like DirectoryInfo, all the methods of FileInfo are instance methods.

Table 21-3 lists the principal methods of the File class, and Table 21-4 lists the important members of the FileInfo class.

Table 21-3. Principal public static methods of the File class

Method

Use

AppendText( )

Creates a StreamWriter that appends text to the specified file.

Copy( )

Copies an existing file to a new file.

Create( )

Creates a file in the specified path.

CreateText( )

Creates a StreamWriter that writes a new text file to the specified file.

Delete( )

Deletes the specified file.

Exists( )

Returns true if the specified file exists.

GetAttributes( )

SetAttributes( )

Gets the FileAttributes of the specified file; sets the FileAttributes of the specified file.

GetCreationTime( )

SetCreationtime( )

Returns the creation date and time of the file; sets the creation date and time.

GetLastAccessTime( )

SetLastAccessTime( )

Returns the last time the specified file was accessed; sets the last time the specified file was accessed.

GetLastWriteTime( )

SetLastWriteTime( )

Returns the last time the specified file was written to; sets the last time the specified file was written to.

Move( )

Moves a file to a new location; can be used to rename a file.

OpenRead( )

Public static method that opens a FileStream on the file.

OpenWrite( )

Creates a read/write Stream on the specified path.

Table 21-4. Methods and properties of the FileInfo class

Method or Property

Use

Attributes( )

Inherits from FileSystemInfo; gets or sets the attributes of the current file.

CreationTime

Inherits from FileSystemInfo; gets or sets the creation time of the current file.

Directory

Public property that gets an instance of the parent directory.

Exists

Public property Boolean value, true if the directory exists.

Extension

Public property inherited from FileSystemInfo; the file extension.

FullName

Public property inherited from FileSystemInfo; the full path of the file or directory.

LastAccessTime

Public property inherited from FileSystemInfo; gets or sets the last access time.

LastWriteTime

Public property inherited from FileSystemInfo; gets or sets the time when the current file or directory was last written to.

Length

Public property that gets the size of the current file.

Name

Public property Name of this DirectoryInfo instance.

AppendText( )

Public method that creates a StreamWriter that appends text to a file.

CopyTo( )

Public method that copies an existing file to a new file.

Create( )

Public method that creates a new file.

Delete( )

Public method that permanently deletes a file.

MoveTo( )

Public method to move a file to a new location; can be used to rename a file.

Open( )

Public method that opens a file with various read/write and sharing privileges.

OpenRead( )

Public method that creates a read-only FileStream.

OpenText( )

Public method that creates a StreamReader that reads from an existing text file.

OpenWrite( )

Public method that creates a read/write FileStream.

Example 21-2 modifies Example 21-1, adding code to get a FileInfo object for each file in each subdirectory. That object is used to display the name of the file, along with its length and the date and time it was last accessed.

Example 21-2. Exploring files and subdirectories

namespace Programming_CSharp
{
   using System;
   using System.IO;

   class Tester
   {
      public static void Main(  )
      {
         Tester t = new Tester(  );

         // choose the initial subdirectory
         string theDirectory = @"c:WinNT";

         // call the method to explore the directory,
         // displaying its access date and all
         // subdirectories
         DirectoryInfo dir = new DirectoryInfo(theDirectory);

         t.ExploreDirectory(dir);

         // completed. print the statistics
         Console.WriteLine(
            "

{0} files in {1}  directories found.
",
            fileCounter,dirCounter);
      }
        
      // Set it running with a directoryInfo object
      // for each directory it finds, it will call 
      // itself recursively
      private void ExploreDirectory(DirectoryInfo dir)
      {
         indentLevel++;  // push a directory level

         // create indentation for subdirectories
         for (int i = 0; i < indentLevel; i++)
            Console.Write("  "); // two spaces per level

         // print the directory and the time last accessed
         Console.WriteLine("[{0}] {1} [{2}]
", 
            indentLevel, dir.Name, dir.LastAccessTime);

         // get all the files in the directory and
         // print their name, last access time, and size
         FileInfo[] filesInDir = dir.GetFiles(  );
         foreach (FileInfo file in filesInDir)
         {
            // indent once extra to put files
            // under their directory
            for (int i = 0; i < indentLevel+1; i++)
               Console.Write("  "); // two spaces per level

            Console.WriteLine("{0} [{1}] Size: {2} bytes", 
               file.Name,
               file.LastWriteTime,
               file.Length);
            fileCounter++;
         }


         // get all the directories in the current directory
         // and call this method recursively on each
         DirectoryInfo[] directories = dir.GetDirectories(  );
         foreach (DirectoryInfo newDir in directories)
         {
            dirCounter++;  // increment the counter
            ExploreDirectory(newDir);
         }
         indentLevel--; // pop a directory level
      }

      // static member variables to keep track of totals
      // and indentation level
      static int dirCounter = 1;
      static int indentLevel  = -1; // so first push = 0
      static int fileCounter = 0;
   }
}

Output (excerpt):
[0] WinNT [5/1/2001 3:34:01 PM]

  Active Setup Log.txt [4/20/2001 10:42:22 AM] Size: 10620 bytes
  actsetup.log [4/20/2001 12:05:02 PM] Size: 8717 bytes
  Blue Lace 16.bmp [12/6/1999 4:00:00 PM] Size: 1272 bytes
    [2] Wallpaper [5/1/2001 3:14:32 PM]
      Boiling Point.jpg [4/20/2001 8:30:24 AM] Size: 28871 bytes
      Chateau.jpg [4/20/2001 8:30:24 AM] Size: 70605 bytes
      Windows 2000.jpg [4/20/2001 8:30:24 AM] Size: 129831 bytes

8590 files in 363  directories found.

The example is initialized with the name of the C:WinNT directory. It prints information about all the files in that directory and then recursively explores all the subdirectories and all their subdirectories (your output might differ). This can take quite a while to run because the WinNT directory tree is rather large (363 subdirectories on my machine, as shown in the output).

Modifying Files

As you can see from Figures 21-3 and 21-4, it is possible to use the FileInfo class to create, copy, rename, and delete files. The next example will create a new subdirectory, copy files in, rename some, delete others, and then delete the entire directory.

Tip

To set up these examples, create a est directory and copy the media directory from WinNT into the test directory. Do not work on files in WinNT directly; when working with system files you want to be extraordinarily careful.

The first step is to create a DirectoryInfo object for the test directory:

string theDirectory = @"c:	estmedia";
DirectoryInfo dir = new DirectoryInfo(theDirectory);

Next, create a subdirectory within the test directory by calling CreateSubDirectory on the DirectoryInfo object. You get back a new DirectoryInfo object, representing the newly created subdirectory:

string newDirectory = "newTest";
DirectoryInfo newSubDir = 
   dir.CreateSubdirectory(newDirectory);

You can now iterate over the test and copy files to the newly created subdirectory:

FileInfo[] filesInDir = dir.GetFiles(  );
foreach (FileInfo file in filesInDir)
{
   string fullName = newSubDir.FullName +  
      "\" + file.Name;
   file.CopyTo(fullName);
   Console.WriteLine("{0} copied to newTest",
      file.FullName);
}

Notice the syntax of the CopyTo method. This is a method of the FileInfo object. You pass in the full path of the new file, including its full name and extension.

Once you’ve copied the files, you can get a list of the files in the new subdirectory and work with them directly:

filesInDir = newSubDir.GetFiles(  );
foreach (FileInfo file in filesInDir)
{

Create a simple integer variable named counter and use it to rename every other file:

if (counter++ %2 == 0)
{
    file.MoveTo(fullName + ".bak");
    Console.WriteLine("{0} renamed to {1}",
        fullName,file.FullName);
}

You rename a file by “moving” it to the same directory but with a new name. You can, of course, move a file to a new directory with its original name, or you can move and rename at the same time.

You’ll rename every other file, and you’ll delete the ones you don’t rename:

file.Delete(  );
Console.WriteLine("{0} deleted.",
    fullName);

Once you’re done manipulating the files, you can clean up by deleting the entire subdirectory:

newSubDir.Delete(true);

The Boolean parameter determines whether this is a recursive delete. If you pass in false, and if this directory has subdirectories with files in it, it will throw an exception.

Example 21-3 lists the source code for the complete program. Be careful when running this; when it is done, the subdirectory is gone. To see the renaming and deletions, either put a breakpoint on the last line or remove the last line.

Example 21-3. Creating a subdirectory and manipulating files

newSubDir.Delete(true);

namespace Programming_CSharp
{
   using System;
   using System.IO;

   class Tester
   {
      public static void Main(  )
      {
         // make an instance and run it
         Tester t = new Tester(  );
         string theDirectory = @"c:	estmedia";
         DirectoryInfo dir = new DirectoryInfo(theDirectory);
         t.ExploreDirectory(dir);
      }
        
      // Set it running with a directory name
      private void ExploreDirectory(DirectoryInfo dir)
      {

         // make a new subdirectory
         string newDirectory = "newTest";
         DirectoryInfo newSubDir = 
            dir.CreateSubdirectory(newDirectory);

         // get all the files in the directory and
         // copy them to the new directory
         FileInfo[] filesInDir = dir.GetFiles(  );
         foreach (FileInfo file in filesInDir)
         {
            string fullName = newSubDir.FullName +  
               "\" + file.Name;
            file.CopyTo(fullName);
            Console.WriteLine("{0} copied to newTest", 
               file.FullName);
         }

         // get a collection of the files copied in
         filesInDir = newSubDir.GetFiles(  );

         // delete some and rename others
         int counter = 0;
         foreach (FileInfo file in filesInDir)
         {
            string fullName = file.FullName;

            if (counter++ %2 == 0)
            {
               file.MoveTo(fullName + ".bak");
               Console.WriteLine("{0} renamed to {1}",
                  fullName,file.FullName);
            }
            else
            {
               file.Delete(  );
               Console.WriteLine("{0} deleted.", 
                  fullName);
            }
         }

         newSubDir.Delete(true); // delete the subdirectory 
      }
   }
}

Output (excerpts):
c:	estmediaBach's Brandenburg Concerto No. 3.RMI
        copied to newTest
c:	estmediaBeethoven's 5th Symphony.RMI copied to newTest
c:	estmediaBeethoven's Fur Elise.RMI copied to newTest
c:	estmediacanyon.mid copied to newTest
c:	estmedia
ewTestBach's Brandenburg Concerto 
        No. 3.RMI renamed to 
c:	estmedia
ewTestBach's Brandenburg Concerto 
        No. 3.RMI.bak
c:	estmedia
ewTestBeethoven's 5th Symphony.RMI deleted.
c:	estmedia
ewTestBeethoven's Fur Elise.RMI renamed to 
c:	estmedia
ewTestBeethoven's Fur Elise.RMI.bak
c:	estmedia
ewTestcanyon.mid deleted.
..................Content has been hidden....................

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