[ Team LiB ] Previous Section Next Section

21.1 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).

21.1.1 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 that 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.

GetLastAccessTime() SetLastAccessTime()

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

GetLastWriteTime() SetLastWriteTime()

Returns and 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, which is true if the directory exists.

Extension

Public property inherited from FileSystemInfo; i.e., the file extension.

FullName

Public property inherited from FileSystemInfo; i.e., 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.

21.1.2 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.

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

string path = Environment.GetEnvironmentVariable("SystemRoot");
DirectoryInfo dir = new DirectoryInfo(path);

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, 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, 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
   {

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

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

         // choose the initial subdirectory
         string theDirectory = 
            Environment.GetEnvironmentVariable("SystemRoot");

         // 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(
            "\n\n{0} directories found.\n",
            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}]\n", 
            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
      }
   }
}

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 (%SystemRoot%, usually C:\WinNT or C:\Windows) 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 %SystemRoot% are displayed. When ExploreDirectory finally returns, the calling method prints a summary.

21.1.3 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; 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 and sets the FileAttributes of the specified file.

GetCreationTime( ) SetCreationtime( )

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

GetLastAccessTime( ) SetLastAccessTime( )

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

GetLastWriteTime( ) SetLastWriteTime( )

Returns and 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 that is true if the directory exists.

Extension

Public property inherited from FileSystemInfo; i.e., the file extension.

FullName

Public property inherited from FileSystemInfo; i.e., 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 write-only 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
   {

      // 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;

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

         // choose the initial subdirectory
         string theDirectory = 
            Environment.GetEnvironmentVariable("SystemRoot");

         // 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(
            "\n\n{0} files in {1}  directories found.\n",
            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}]\n", 
            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
      }
   }
}

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 %SystemRoot% 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 %SystemRoot% directory tree is rather large (363 subdirectories on my machine, as shown in the output).

21.1.4 Modifying Files

As you can see from Tables Table 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.

To set up these examples, create a \test directory and copy the media directory from WinNT or Windows into the \test directory. Do not work on files in the system root 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:\test\media";
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. 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.

Rename every other file, and 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 throws 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
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:\test\media";
         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:\test\media\Bach's Brandenburg Concerto No. 3.RMI
        copied to newTest
c:\test\media\Beethoven's 5th Symphony.RMI copied to newTest
c:\test\media\Beethoven's Fur Elise.RMI copied to newTest
c:\test\media\canyon.mid copied to newTest
c:\test\media\newTest\Bach's Brandenburg Concerto 
        No. 3.RMI renamed to 
c:\test\media\newTest\Bach's Brandenburg Concerto 
        No. 3.RMI.bak
c:\test\media\newTest\Beethoven's 5th Symphony.RMI deleted.
c:\test\media\newTest\Beethoven's Fur Elise.RMI renamed to 
c:\test\media\newTest\Beethoven's Fur Elise.RMI.bak
c:\test\media\newTest\canyon.mid deleted.    
    [ Team LiB ] Previous Section Next Section