15.7. Files and Directories

Now let's look at the File and Directory classes of the System.IO namespace. C# provides a rich API for working with files and directories. The System.IO.File and System.IO.Directory classes are used to model files and directories. Unlike Java, C# makes a distinction between a file and a directory. The File class consists of all static methods. The static methods of the File class perform security checks on all methods. You should use the FileInfo class if you're going to reuse a file instance so that you can avoid the repetitive security checks.

The following sections briefly discuss the static methods of this class. Some of the File static methods do not throw an IOException, instead throwing a more relevant exception.

15.7.1. AppendText

Listing 15.5 shows the usage of the AppendText function. The static AppendText method is equivalent to calling new StreamWriter (path, true), where the second Boolean parameter indicates whether the file is to be opened for appending. The method returns a StreamWriter.

Listing 15.5. Using File.AppendText (C#)
using System;
using System.IO;

public class Test {
  public static void Main(string[] args) {
    AppendText();
  }
  static void AppendText() {
    StreamWriter sw = null;
    try {
      string PATH = "c:\Book\test.txt";
      sw = File.AppendText(PATH);
      sw.WriteLine("File static method");
      sw.Flush();
    } catch (Exception e) {
      throw new IOException(e.StackTrace);
    } finally {
      sw.Close();
    }
  }
}

Note that even though the static function is used to return a StreamWriter, you must still call Close() on the writer to ensure that all the data is correctly written out to the stream.

15.7.2. Exists, Copy, Move, and Delete

The Exists method takes a single path parameter. This method does not throw a FileNotFoundException; instead, it throws exceptions only if the path parameter is malformed (the equivalent of a java.lang.IllegalArgumentException). Also, the parameter must point to a file and not a directory. Therefore, the following lines return true and false, respectively:

Console.WriteLine(File.Exists(TO_FILE));
Console.WriteLine(File.Exists("c:\"));

The Copy method throws several exceptions (based on the underlying error). Whereas an IOException is thrown if the destination file exists, a FileNotFoundException is thrown if the source file is not found. By default, the Copy method does not overwrite the destination file. So if you have called Copy once on a target file, then calling Copy again will throw an IOException. To overwrite the target file, use the three-parameter call. To avoid getting an IOException upon repetitive copying of a file to the same destination, use File.Copy(source, destination, overwrite) instead of File.Copy(source, destination).

And again, when writing actual classes that use these File static methods, it's imperative to catch the correct exception and log the exception. We have omitted exception handling code from Listing 15.6 so as to accentuate the File API calls. Note that the destination must be a file; it cannot be a directory.

The Move method works across disk volumes, and it does not throw an exception if the source and destination are the same. The Delete method does not throw an exception if the file to be deleted does not exist.

Listing 15.6. Using the Move, Copy, Delete, and Exists Methods (C#)
using System;
using System.IO;

public class Test {
  public const string TO_FILE = "c:\book\test.txt";
  public const string COPY_TO_FILE = "c:\book\test1.cop";
  public const string MOVE_TO_FILE = "c:\book\test1.mov";

  public static void Main(string[] args) {

    Console.WriteLine(File.Exists(TO_FILE));
    Console.WriteLine(File.Exists("c:\"));
    File.Copy(TO_FILE, COPY_TO_FILE, true);
    File.Move(TO_FILE, MOVE_TO_FILE);
    File.Delete(TO_FILE);
  }
}

15.7.3. Open, OpenWrite, OpenText, and OpenRead

These methods return either a FileStream or a StreamReader for writing or reading. Listing 15.7 shows the use of the OpenText method.

Listing 15.7. Using the OpenText() Method (C#)
using System;
using System.IO;

public class Test {
  public const string TO_FILE = "c:\book\test.txt";
  public static void Main(string[] args) {
    StreamReader sr = File.OpenText(TO_FILE);
    while (sr.Peek() != 1)
      Console.WriteLine(sr.ReadLine());
  }
}

The FileInfo class is more similar to the java.io.File class. All methods on this class are instance-based. Most of the static methods of the File class are duplicated as instance methods of the FileInfo class.

The System.IO.Directory class should be used for basic directory-based operations. Again, the class has only static methods, which make the appropriate security checks. To avoid repeating security checks for a directory that has already been scanned for security, use the DirectoryInfo class. Listing 15.8 shows operations for manipulating directories.

Listing 15.8. Manipulating Directories (C#)
using System;
using System.IO;

public class Test {
  const string BASE_PATH = "c:\chuck";
  public static void Main(string[] args) {
    //Create directory
    DirectoryInfo dir = Directory.CreateDirectory(BASE_PATH);
    //Create subdirectories
    string path = null;
    for (int i =0; i < 10; ++i) {
      path = BASE_PATH+ "\Sub"+i;
      DirectoryInfo subdir = dir.CreateSubdirectory("Sub"+i);
      Directory.SetCurrentDirectory(subdir.FullName);
      for (int j = 0; j < i; ++j) {
        subdir.CreateSubdirectory("SubSub"+j);
      }
      Directory.SetCurrentDirectory(path);
    }
    Console.WriteLine(Directory.GetCurrentDirectory());
    CreateFile();
    Directory.Delete(BASE_PATH+"\Sub"+4, true);
    Directory.Move(BASE_PATH+"\Sub"+3,BASE_PATH+"\Sub"+4);
  }

  private static void CreateFile() {
    FileStream fs = new FileStream("test.txt",
      FileMode.OpenOrCreate, FileAccess.ReadWrite,
      FileShare.None);
    StreamWriter sw = new StreamWriter(fs);
    sw.WriteLine("Testing");
    sw.Flush();
    sw.Close();
  }
}

In Listing 15.8 we first create a directory using the following:

DirectoryInfo dir = Directory.CreateDirectory (BASE_PATH);

The for loop then uses the DirectoryInfo object to create subdirectories. Note that when creating a subdirectory you do not specify the fully qualified directory name (the UNC name); instead, you specify only the relative leaf directory name.

for (int i =0; i < 10; ++i) {
  path = BASE_PATH+"\Sub"+i;
  DirectoryInfo subdir = dir.CreateSubdirectory("Sub"+i);
  for (int j = 0; j < i; ++j) {
    subdir.CreateSubdirectory("SubSub"+j);
  }
  Directory.SetCurrentDirectory(path);
}

The next part of the code sets the current working directory and then deletes a directory (recursively) using

Directory.Delete(BASE_PATH+"\Sub"+4, true);

Then subdirectory 3 and its contents are moved to subdirectory 4.

As you can see, manipulating directories is a breeze in C#. Listing 15.9 shows how to browse directories to get useful information.

Listing 15.9. A Recursive Approach to Browsing Files in a Directory (C#)
using System;
using System.IO;

public class Test {
  const string BASE_PATH = "c:\chuck";
  public static void Main(string[] args) {
       BrowseDirectories(BASE_PATH);
  }
  private static void BrowseDirectories(String dirPath) {
    string[] directories = Directory.GetDirectories(dirPath);
    foreach (string dir in directories) {
      string[] files = Directory.GetFiles(dir, "*.txt");
      foreach(string file in files) {
        Console.WriteLine("File Path "+file);
      }
      BrowseDirectories(dir);
    }
  }
}

Note that we use the Directory.GetFiles (path, searchpattern) call to recursively filter out files having a certain extension from a given directory. To search for a directory, use the GetDirectoryNames instance method of IsolatedStorageFile. The GetDirectoryNames method takes a string representing a search pattern. Both single-character (?) and multicharacter (*) wildcard characters are supported. These wildcard characters cannot appear in the path portion of the name; that is, directory1/*ect* is a valid search string, but *ect*/directory2 is not, according to the Microsoft .NET documentation.

15.7.4. Asynchronous I/O

All the operations we have seen so far are synchronous in nature; the calling thread blocks until the synchronous I/O operation is complete. Many times you want the calling thread to carry on with whatever it was doing after making the I/O call. In general, I/O calls are slower than in-memory calls. For most applications, it is I/O operations that slow things down, whether file or network access. Therefore, applications that are built with asynchronous I/O will be faster than their synchronous counterparts.

Listing 15.10 shows how the FileStream class is used to make both synchronous and asynchronous calls. Note that as of JDK 1.3.x, the only way to simulate asynchronous I/O in Java is through threads. However, JDK 1.4 has the java.nio.* package, which offers built-in asynchronous I/O.

Listing 15.10. Asynchronous I/O (C#)
using System;
using System.IO;
using System.Threading;

public class FileReader {

  const string PATH = "c://book//test.jsp";
  public void SyncCall() {
       FileStream fs = new FileStream(
         PATH,
         FileMode.Open,
         FileAccess.Read,
         FileShare.Read,
         1024,
         false
         );
       int × = 0;
       while ((× = fs.ReadByte()) != 1) {}
       fs.Close();
       Console.WriteLine("Sync oper finished "+DateTime.Now);
   }

   public void AsyncCall() {
     FileStream fs = new FileStream(
       PATH,
       FileMode.Open,
       FileAccess.Read,
       FileShare.Read,
       1024,
       true
       );
     WordDocState state = new WordDocState();
     state.fs = fs;
     state.content = new byte[fs.Length];
     state.resetEvent = new AutoResetEvent(false);
     AsyncCallback readDocCallBack = new
            AsyncCallback(ReadDocCallback);
     fs.BeginRead(state.content, 0, (int)state.fs.Length,
                                 readDocCallBack, state);
     state.resetEvent.WaitOne();
     Console.WriteLine("Async oper finished "+DateTime.Now);
   }
   private void ReadDocCallback(IAsyncResult asyncResult){
     WordDocState state = (WordDocState) asyncResult.AsyncState;
     FileStream stream = state.fs;
     int bytesRead = stream.EndRead(asyncResult);
     stream.Close();
     state.resetEvent.Set();
   }

   public class WordDocState {
     public FileStream fs;
     public byte[] content;
     public AutoResetEvent resetEvent;
   }

   public static void Main(string[] args) {
     FileReader fr = new FileReader();
     fr.AsyncCall();
     fr.SyncCall();
   }
 }

The asynchronous calls start with the following call:

fs.BeginRead(state.content, 0, (int)state.fs.Length,
                                        readDocCallBack, state);

To the BeginRead method we pass callback, which calls EndRead, which waits for the asynchronous call to end.

..................Content has been hidden....................

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