Hour 14. Using Files and Streams


What You’ll Learn in This Hour

Files and directories

Reading and writing data


In Hour 10, “Working with Arrays and Collections,” and Hour 13, “Understanding Query Expressions,” you learned how applications could work with data stored in collections and how to query and manipulate that data. Although these are common activities, many applications need to store or retrieve data from files on a disk.

The .NET Framework treats files as a stream of data. A stream is a sequential flow of packets of data, represented as bytes. Data streams have an underlying storage medium, typically called a backing store, which provides a source for the stream. Fortunately, the .NET Framework makes working with files and directories easier by the File, Directory, and Path classes provided by the .NET Framework.

The System.IO namespace contains all the classes you need to work with both buffered and unbuffered streams. Buffered streams enable the operating system to create its own internal buffer that it uses to read and write data in whatever increments are most efficient.

In this hour, you learn how to work with files, using the File, Directory, and Path classes to explore and manage the file system and for reading and writing files. You also learn how you can use the Stream class, or any of its derived classes, to perform more complex read and write operations.

Files and Directories

You can think of a file as a sequence of bytes having a well-defined name and a persistent backing store. Files are manipulated through directory paths, disk storage, and file and directory names. The .NET Framework provides several classes in the System.IO namespace that make working with files easy.

Working with Paths

A path is a string that provides the location of a file or directory, and can contain either absolute or relative location information. An absolute path fully specifies a location, whereas a relative path specifies a partial location. When using relative paths, the current location is the starting point when locating the file specified.


Note: Current Location

Every process has a processwide “current location,” which is usually, but not always, the location where the process executable was loaded.


The Path class provides static methods that perform operations on path strings in a cross-platform manner. Although most Path class members don’t interact with the file system, they do validate that the specified path string contains valid characters. Table 14.1 shows the commonly used methods.

Table 14.1. Commonly Used Methods of the Path Class

Image

Interacting with the Windows Special Directories

The Windows operating system includes many “special” folders frequently used by applications. Typically, the operating system sets these folders; however, a user can also explicitly set them when installing a version of Windows. As a result, many might not have the same location or name on any given machine.

These special directories are listed in the Environment.SpecialFolder enumeration. Some of the common folders are shown in Table 14.2.

Table 14.2. Common Environment.SpecialFolder Values

Image

The enumeration simply provides a consistent way to reference these folders; to get the actual folder path for a given folder you should use the Environment.GetFolderPath method. For example, to find the path to the user’s Documents directory, use the following code:

string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

The DirectoryInfo and FileInfo Classes

The DirectoryInfo and FileInfo classes both derive from the FileSystemInfo class, which contains the methods common to file and directory manipulation and can represent either a file or a directory. When a FileSystemInfo is instantiated, the directory or file information is cached, so you must refresh it using the Refresh method to ensure current information.

The DirectoryInfo class contains instance members that provide a number of properties and methods for performing common operations such as copying, moving, creating, and enumerating directories. The commonly used methods and properties of the DirectoryInfo class are listed in Table 14.3.

Table 14.3. Commonly Used DirectoryInfo Members

Image

Listing 14.1 shows how the DirectoryInfo class might perform some common operations.

Listing 14.1. Using the DirectoryInfo Class


public class DirectoryInfoExample
{
   public static void Main()
   {
      string tempPath = Path.GetTempFileName();

      DirectoryInfo directoryInfo = new DirectoryInfo(tempPath);
      try
      {
         if (directoryInfo.Exists)
         {
            Console.WriteLine("The directory already exists.");
         }
         else
         {
            directoryInfo.Create();
            Console.WriteLine("The directory was successfully created.");
            directoryInfo.Delete();
            Console.WriteLine("The directory was deleted.");
         }
      }
      catch (IOException e)
      {
         Console.WriteLine("An error occurred: {0}", e.Message);
      }
   }
}


The FileInfo class contains instance members that provide a number of properties and methods for performing common file operations such as copying, moving, creating, and opening files. The commonly used methods and properties are listed in Table 14.4.

Table 14.4. Commonly Used FileInfo Members

Image

Listing 14.2 shows how the FileInfo class might perform some common operations.

Listing 14.2. Using the FileInfo Class


public class FileInfoExample
{
   public static void Main()
   {
      string tempFile = Path.GetTempFileName();

      FileInfo fileInfo = new FileInfo(tempFile);
      try
      {
         if (!fileInfo.Exists)
         {
            using (StreamWriter writer = fileInfo.CreateText())
            {
               writer.WriteLine("Line 1");
               writer.WriteLine("Line 2");
            }
         }

         fileInfo.CopyTo(Path.GetTempFileName());
         fileInfo.Delete();
      }
      catch (IOException e)
      {
         Console.WriteLine("An error occurred: {0}", e.Message);
      }
   }
}



Note: Streams Are Disposable

You should be sure to dispose of the stream when you finish using it by calling the Close method. You can also wrap the streams in a using statement, which is the preferred way to ensure the stream is closed correctly.


Reading and Writing Data Using the File Class

Because reading and writing data, whether it is text or binary, from files is a common task, the File class provides several methods that make this even more convenient than working directly with streams.

To read or write binary data, you can use the ReadAllBytes and WriteAllBytes methods, respectively. These methods open the file, read or write the bytes, and then close the file. The code shown in Listing 14.8 performs the same actions as Listing 14.5 using the ReadAllBytes and WriteAllBytes methods.

Listing 14.8. Binary Reads and Writes Using the File Class


public class BinaryReaderWriterFile
{
   public static void Main()
   {
      string tempPath = Path.GetTempFileName();
      string tempPath2 = Path.GetTempFileName();

      if (File.Exists(tempPath))
      {
         byte[] data = File.ReadAllBytes(tempPath);
         File.WriteAllBytes(tempPath2, data);
      }
   }
}


Reading and writing text data is just as easy using the ReadAllLines and ReadAllText methods for reading and the WriteAllLines and WriteAllText methods for writing. The ReadAllLines method reads all the lines from the file into a string array, where each line is a new element in the array, whereas the ReadAllText reads all the lines into a single string.

The WriteAllLines method writes each element of a string array to a file, whereas the WriteAllText method writes the contents of a string to the file. Both of these create a new file or overwrite the file if it already exists. To append text to an existing file, you can use the AppendAllLines or AppendAllText methods. If you need to open a stream, you can use the AppendText method.

The code shown in Listing 14.9 performs the same actions as Listing 14.7 using the ReadAllLines and WriteAllLines methods.

Listing 14.9. Text Reads and Writes Using ReadAllLines and WriteAllLines


public class TextReaderWriterFile
{
   public static void Main()
   {
      string tempPath = Path.GetTempFileName();
      string tempPath2 = Path.GetTempFileName();

      if (File.Exists(tempPath))
      {
         string[] data = File.ReadAllLines(tempPath);
         File.WriteAllLines(tempPath2, data);
      }
   }
}


The one drawback to using ReadAllLines, or even ReadAllText, is that the entire file must first be read into memory. To resolve this issue and return an IEnumerable<string> collection, you can use the ReadLines method. Because this method returns an IEnumerable<string>, you can start to enumerate the returned collection immediately, before the whole collection is returned. The code shown in Listing 14.10 performs the same actions as Listing 14.9 using the File.ReadLines method.

Listing 14.10. Text Reads and Writes Using WriteAllLines and ReadLines


public class TextReaderWriterFile
{
   public static void Main()
   {
      string tempPath = Path.GetTempFileName();
      string tempPath2 = Path.GetTempFileName();

      if (File.Exists(tempPath))
      {
         File.WriteAllLines(tempPath, File.ReadLines(tempPath2));
      }
   }
}


Summary

In this hour, you learned how to work with streams to read and write text and binary files. Although you focused only on using the FileStream and StreamWriter classes, the mechanisms used for reading and writing using FileStream is essentially the same for any Stream derived class.

You also learned how the .NET Framework makes working with files, directories, and string paths simple through the File, Directory, FileInfo, DirectoryInfo, and Path classes.

Q&A

Q. What is a stream?

A. A stream is a sequential flow of packets of data represented as bytes. Data streams have an underlying storage medium, typically called a backing store, which provides a source for the stream.

Q. What is the difference between a relative path and an absolute path?

A. An absolute path fully specifies a location, whereas a relative path specifies a partial location. When using relative paths, the current location is the starting point when locating the file specified.

Q. What is the FileSystemInfo class used for?

A. The FileSystemInfo class contains methods common to file and directory manipulation and can represent either a file or directory. It is the base class for the DirectoryInfo and FileInfo classes.

Q. How is the Directory class different from the DirectoryInfo class?

A. The Directory class provides only static methods, whereas the DirectoryInfo class provides only instance methods and caches the information retrieved for the specified directory.

Q. What is the difference between a binary file and a text file?

A. A binary file is simply a stream of bytes, whereas a text file is known to contain only text data.

Workshop

Quiz

1. How do the Path class members interact directly with the file system?

2. What method should be used on a FileSystemInfo instance to update the cached information it contains?

3. What is the difference between the EnumerateDirectories method on the DirectoryInfo and Directory classes?

4. What is the return type of the File.OpenRead method?

5. What is the return type of the File.OpenText method?

6. What is the difference between the File.ReadAllLines method and the File.ReadLines method?

Answers

1. Most Path class members don’t interact with the file system; they do, however, validate that the specified path string contains valid characters.

2. The FileSystemInfo class contains a Refresh method that should be used to update the cached file or directory information.

3. The DirectoryInfo.EnumerateDirectories returns an IEnumerable<DirectoryInfo>, whereas Directory.EnumerateDirectories returns an IEnumerable<string>.

4. The File.OpenRead method returns a FileStream opened to the specified file for reading only.

5. The File.OpenText method returns a StreamReader opened to the specified text file.

6. File.ReadAllLines must read the entire file into memory and returns a string array containing the lines whereas File.ReadLines returns an IEnumerable<string> enabling you to start enumerating the collection before the entire file is read.

Exercises

1. Modify the PhotoCollection class of the PhotoViewer project by changing the data type of the path field to be a DirectoryInfo. Change the Path property so that the get accessor returns the value of the FullName property and the set accessor creates a new DirectoryInfo instance from value after validating that value is not null or an empty string. Also change the constructor so that it uses the set accessor rather than setting the backing field directly.

2. Add a private method named Update to the PhotoCollection class and call it from the constructor right after the Path property is set. This method should perform the following actions:

a. Clear the collection.

b. If path.Exists is true, enumerate over all files in the directory whose extension is “.jpg” and add a new Photo instance to the collection. This code should be in a try-catch block that catches a DirectoryNotFoundException. The catch handler should contain the following code:

System.Windows.MessageBox.Show("No Such Directory");

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

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