Data Streams

.Data Streams
Backing Store Streams | Decorator Streams | Stream Adapters | Stream Class | File Stream | File, Directories, Paths | Memory Stream | Memory-Mapped Files | Pipe Stream | Buffered Stream | Text Adapters | Binary Adapters | FileSystemWatcher Class | Isolated Storage

"A stream is an abstraction for a sequence of bytes. Streams provide a more consistent way to interact with the various types of underlying data sources. Streams support reading, writing, and some streams support seeking. Async methods where created for the Stream class in .NET 4.5 which allow for resource-intensive I/O operations without blocking the main thread."

.Streams
Data Streams

A stream is a data source abstraction which allows data from diverse sources to be processed in a similar fashion. All classes that represent streams are derived from the Stream base class. Components of the stream architecture can be broken down into three categories:

  1. Backing Store Streams - is the end point for the I/O operation. These streams are hard-wired to a specific type of backing store, such as a file or a network source of data.

  2. Decorator Streams - are based on the Decorator Design Pattern that "dynamically adds functionality to an object". These streams transform the data from an existing string. The data stream can cycle through multiple decorator streams, such a file which is encrypted and then compressed.

  3. Adapters - transform the bytes which used in Backing Store Streams and Decorator Streams into higher levels of data typically used by applications, such as text, XML, or the C# primitive data types.

Streams process data serially, either one byte at a time, or in a small block of bytes. So regardless of the size of the backing store, processing the associated stream requires little memory. Additional characteristics of streams include:

  1. Streams provide a consistent programming interface across a variety of data sources.

  2. Streams work exclusively at the byte level. Adapters are needed to work with the data at a higher level.

  3. Streams contain properties to indicate if the particular stream supports reading, writing, or seeking.

  4. Inherently streams are not thread-safe. However the Stream class offers a static Synchronized method which encapsulates the stream with concurrency protection.

  5. Network streams support time-out capability which can be specified in milliseconds. File and Memory streams do not support time-outs.

  6. Streams gained support for asynchronous reading and writing in .NET 4.5. The async stream methods allow for resource-intensive I/O operations without blocking the main thread.

  7. Stream buffers are automatically flushed when the stream is closed.

  8. Streams must be disposed after use to release the underlying resources. Disposal can be guaranteed by instantiating streams within a using statement. Alternatively the Dispose method can be called from inside the final clause of the try statement. See the Exception Handling
  9. article for more details about disposing of resources in the finally clause.

Backing Store Streams

Backing store streams provide support for interfacing with particular types of data stores. It is also possible to have a null stream which contains no backing store and acts as a data sink. Support for additional backing store streams are included in the following namespaces:

  1. System.IO
    • FileStream - exposes a Stream around a file, supporting both synchronous and asynchronous read and write operations.
    • MemoryStream - creates a stream whose backing store is memory.
  2. System.IO.Pipes
    • PipeStream - exposes a Stream object around a pipe, which supports both anonymous and named pipes.
    • AnonymousPipeClientStream - exposes the client side of an anonymous pipe stream, which supports both synchronous and asynchronous read and write operations.
    • AnonymousPipeServerStream - exposes a stream around an anonymous pipe, which supports both synchronous and asynchronous read and write operations.
    • NamedPipeClientStream - exposes a Stream around a named pipe, which supports both synchronous and asynchronous read and write operations.
    • NamedPipeServerStream - exposes a Stream around a named pipe, supporting both synchronous and asynchronous read and write operations.
  3. System.Net.Sockets
    • NetworkStream - provides the underlying stream of data for network access.
Top




Decorator Streams

Decorator streams provide additional functionality to the stream at runtime. Decorator streams can be chained together to provide multiple transformations to the stream. Decorator streams free the backing store streams from having to provide compression, encryption, etc. Decorator streams are included in the following namespaces:

  1. System.IO
    • BufferedStream - adds a buffering layer to read and write operations on another stream.
  2. System.IO.Compression
    • DeflateStream - provides methods and properties for compressing and decompressing streams by using the Deflate algorithm.
    • GZipStream- provides methods and properties used to compress and decompress streams.
  3. System.Security.Cryptography
    • CryptoStream - defines a stream that links data streams to cryptographic transformations.
Top




Stream Adapters

The Adapter Pattern allows two incompatible interfaces to communicate. In C# all streams are defined as a series of bytes. Stream adapters provide a higher-level of data abstraction from bytes to characters, strings, primitive values, or XML. For example if you wish to work with a stream of bytes which represent character data, then you would use classes derived from the Text Adapter. For a stream of bytes which represent numeric data, you would use a Binary Adapter. The following adapters are provided by .NET:

  1. System.IO
    • BinaryReader - reads primitive data types as binary values in a specific encoding.
    • BinaryWriter - writes primitive types in binary to a stream and supports writing strings in a specific encoding.

    • TextReader - the abstract base class for reading character only data.
    • TextWriter - the abstract base class for writing character only data.

    • StreamReader - implements a TextReader that reads characters from a byte stream in a particular encoding. The backing store is a stream. Translation is required between bytes and characters.
    • StreamWriter - implements a TextWriter for writing characters to a stream in a particular encoding. The backing store is a stream. Translation is required between bytes and characters.

    • StringReader - implements a TextReader that reads from a string. Backing store is String or StringBuilder. No translation required as backing store in already in character format.
    • StringWriter - implements a TextWriter for writing information to a string. Backing store is String or StringBuilder. No translation required as backing store in already in character format.
  2. System.Xml
    • XmlReader - represents a reader that provides fast, noncached, forward-only access to XML data.
    • XmlWriter - Represents a writer that provides a fast, non-cached, forward-only way to generate streams or files that contain XML data.

Top




Stream Class

All stream classes are derived from the abstract Stream class. A stream's I/O capabilities depend on the stream's underlying data store. A stream can be queried for its capabilities by using the CanRead, CanWrite, and CanSeek properties of the Stream class. Additional useful Stream properties include:

  1. CanTimeout - determines whether the current stream can time out.

  2. Length - gets the length in bytes of the stream.

  3. Position - gets or sets the position within the current stream.

  4. ReadTimeout - a value, in miliseconds, that determines how long the stream will attempt to read before timing out.

  5. WriteTimeout - a value, in miliseconds, that determines how long the stream will attempt to write before timing out.

The Stream class provides the following three read methods. There are also three corresponding write methods.

  1. Read - reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.

  2. ReadByte - Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.

  3. ReadAsync - asynchronously reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.

Additional useful Stream methods include:

  1. CopyTo - reads the bytes from the current stream and writes them to another stream.

  2. CopyToAsync - Asynchronously reads the bytes from the current stream and writes them to another stream.

  3. Dispose - releases all resources used by the Stream.

  4. Finalize - allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.

  5. Flush - clears all buffers for this stream and causes any buffered data to be written to the underlying device.

  6. Seek - sets the position within the current stream.

  7. SetLength - sets the length of the current stream.

  8. Synchronized - creates a thread-safe (synchronized) wrapper around the specified Stream object.

The following example code shows how the different types of streams can be "upcast" to the base Stream class. This allows for reuse of code. Notice the Print method is passed a FileStream and then a MemoryStream and accepts them as the base Stream class. The Stream class property (length) and method (Read) are then used on both the FileStream and MemoryStream inside the Print method.

.Base Stream Class



Upcast FileStream and MemoryStream to Stream class



using System;
using System.IO;
using System.Linq;
using System.Text;

class Program
{
    const int BUFFER_SIZE = 250;
    static int streamCounter = 0;

    static void Main()
    {               
        using (FileStream myFileStream = File.Open(@"C:\myFile.txt", FileMode.Open))
           Print(myFileStream);

        byte[] memoryBuffer = System.Text.Encoding.ASCII.GetBytes("Badfinger - Baby Blue (1972)\n");
        using (MemoryStream stream2 = new MemoryStream(memoryBuffer))       
           Print(stream2);
    }
    
    static void Print(Stream stream)
    {
        // Print length of input stream
        Console.WriteLine("{0}. Stream length is: {1}", ++streamCounter, stream.Length);       

        // Create read byte buffer
        byte[] readBuffer = Enumerable.Repeat(Byte.MinValue, BUFFER_SIZE).ToArray(); 

        // Read raw bytes from stream
        int bytesRead = 0;
        int byteSize = 1;
        while (bytesRead < readBuffer.Length && byteSize > 0)
            bytesRead += byteSize = stream.Read(readBuffer, bytesRead, readBuffer.Length - bytesRead);

        // Convert raw bytes to default (UTF8) characters.
        char[] myChars = Encoding.Default.GetChars(readBuffer);

        // Print buffer contents
        foreach (char myChar in myChars)
          if (myChar != Byte.MinValue)
            Console.Write(myChar);
        Console.WriteLine("");
    }
}

Top




File Stream

The FileStream class can be used to perform I/O operations on files or file-related handles (e.g. standard input, standard output, pipes). The FileStream class has asynchronous methods to perform resource-intensive file operations without blocking the main thread. A FileStream can be instantiated with the FileStream class, or (more easily) with the File class (see more details in the File class section).
A popular FileStream constructors is the one including the three parameters the FileMode ("Creation Mode"), FileAccess ("Read/Write Permissions"), and the FileShare ("Concurrent Sharing Permissions") as shown below. Another commonly used constructor just contains the FileMode and FileAccess parameters, with the FileShare parameter. In which case the FileShare.Read is the default for those FileStream constructors without a FileShare parameter.

FileStream myFileStream = new FileStream("myFile.txt", FileMode.Open, FileAccess.Read, FileShare.Read);

The FileStream constructor contains three parameters which control access to the FileStream. The three access parameters and their enumerations are:

  1. FileMode - specifies creation mode.
    • Append- opens the file if it exists and seeks to the end of the file, or creates a new file.
    • Create - specifies a new file should be created. If the file already exists, it will be overwritten
    • CreateNew - specifies a new file should be created. If the file already exists, an IOException exception is thrown.
    • Open - specifies that the operating system should open an existing file. The ability to open the file is dependent on the value specified by the FileAccess enumeration. A System.IO.FileNotFoundException exception is thrown if the file does not exist.
    • OpenorCreate - specifies that the operating system should open a file if it exists; otherwise, a new file should be created. If the file is opened with FileAccess.Read, FileIOPermissionAccess.Read permission is required. If the file access is FileAccess.Write, FileIOPermissionAccess.Write permission is required. If the file is opened with FileAccess.ReadWrite, both FileIOPermissionAccess.Read and FileIOPermissionAccess.Write permissions are required.
    • Truncate - specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes. This requires FileIOPermissionAccess.Write permission. Attempts to read from a file opened with FileMode.Truncate cause an ArgumentException exception.
  2. FileAccess - specifies read/write permission.
    • Read - read access to the file.
    • ReadWrite - read and write access to the file
    • Write - write access to the file
  3. FileShare - specifies sharing permission. Used to define how much access to grant other processes wanting to use the same file before the current process is finished with it.
    • Delete - allows subsequent deleting of a file.
    • Inheritable - makes the file handle inheritable by child processes.
    • None - declines sharing of the current file. Any request to open the file (by this process or another process) will fail until the file is closed.
    • Read - (Default) allows subsequent opening of the file for reading.
    • ReadWrite - allows subsequent opening of the file for reading or writing.
    • Write - allows subsequent opening of the file for writing.

If the FileShare.ReadWrite is used in the FileStream constructor, the FileStream Lock and Unlock methods should be used by the processes concurrently accessing the FileStream. The Lock method will throw an exception if all or part of the file section has already been locked. The default for the FileShare parameter is FileShare.Read.

Some of the advanced FileStream characteristics are controlled through the use of the following additional parameters in the FileStream constructor:

  1. bufferSize- sets the size of the internal buffer (default is 4 KB).

  2. FileSystemRights - enumeration contains granular system rights which can be dynamically assigned.

  3. FileSecurity - class is an abstraction of the underlying Microsoft Windows file security system. In this system, each file has a discretionary access control list (DACL), which controls access to the file, and a system access control list (SACL), which specifies the access control attempts that are audited. The FileSystemAccessRule and FileSystemAuditRule classes are abstractions of the access control entries (ACEs) that comprise DACLs and SACLs.

  4. FileOptions - enumeration which allows a bitwise combination of its member values.
    • Asynchronous - Indicates that a file can be used for asynchronous reading and writing.
    • DeleteOnClose - Indicates that a file is automatically deleted when it is no longer in use.
    • Encrypted - Indicates that a file is encrypted and can be decrypted only by using the same user account used for encryption.
    • None - Indicates that no additional options should be used when creating a FileStream object.
    • RandomAccess - Indicates that the file is accessed randomly. The system can use this as a hint to optimize file caching.
    • SequentialScan - Indicates that the file is to be accessed sequentially from beginning to end.
    • WriteThrough - Indicates that the system should write through any intermediate cache and go directly to disk.

FileStreams can allow for real asynchronous I/O when the useAsync flag is set to true on the constructor. This is unlike the async methods in the File class which "fake" async I/O by using another thread from the pool to work with the file.

The following program shows how to use the CopyToAsync method to asynchronously copy the files, using async FileStreams, from one directory to another. The code contains EnumerateDirectories which can start enumerating the directories before the collection is completely retrieved. If GetDirectores were used instead, you would have to wait until all the directory names were in the collection before continuing the process. Notice the FileStream is constructed with the default 4096 buffer size followed by a Boolean flag set to "true" (useAsync flag) which allows for "real" asynchronous I/O.

Asynchronous FileStream Copy

using System.IO;

class Program
{
    static void Main()
    {
        string fromDirectory = @"c:\kevin\from";
        string toDirectory = @"c:\kevin\to";

        foreach (string fileName in Directory.EnumerateFiles(fromDirectory))
            CopyFiles(fileName, fromDirectory, toDirectory);
    }

    private static async void CopyFiles(string fileName, string fromDirectory, string toDirectory)
    {
        string filePathTo = toDirectory + fileName.Substring(fileName.LastIndexOf('\\'));
        string filePathFrom = fromDirectory + fileName.Substring(fileName.LastIndexOf('\\'));

        using (FileStream SourceStream = new FileStream(filePathFrom,
               FileMode.Open, FileAccess.Read, FileShare.None, 4096, true))
          using (FileStream DestinationStream = File.Create(filePathTo))
              await SourceStream.CopyToAsync(DestinationStream);
    }
}

Top




File, Directories, Paths

.NET contains various classes for supporting files and file systems. One of the most versatile classes for working with files is the File class. Other classes for I/O support include Directory, FileInfo, DirectoryInfo, Path, and DriveInfo. MSDN has examples of how to perform common I/O tasks in the article Common I/O Tasks.

File Class - Instantiating FileStreams

While a FileStream can be instantiated with FileStream constructors, they can also be instantiated in a simpler fashion using the following File class static methods:

  1. Create - (read/write access) creates or overwrites a file in the specified path.
    using (FileStream fs = File.Create(path))
    
    • File is created if it does not already exist. If file does exist and it is not read-only, the content is completely removed.
    • FileStream has a default FileShare value of None.
    • FileStream has the default 4K buffer size.
    • FileStream has read/write access to all users.

  2. OpenWrite - (write-only access) opens an existing file or creates a new file for writing.
    using (FileStream fs = File.OpenWrite(path))
    
    • File is created if it does not already exist. If file does exist and it is not read-only, the content is left intact and the file pointer is positioned to the beginning of the file. If fewer bytes are subsequently written than exists in the file, then the file contains a mixture of the old and new content.
    • This method is equivalent to the FileStream(String, FileMode, FileAccess, FileShare) constructor overload with file mode set to OpenOrCreate, the access set to Write, and the share mode set to None.
    • FileStream has the default 4K buffer size.

  3. OpenRead - (read only access) opens an existing file for reading.
    using (FileStream fs = File.OpenRead(path))
    
    • This method is equivalent to the FileStream(String, FileMode, FileAccess, FileShare) constructor overload with a FileMode value of Open, a FileAccess value of Read and a FileShare value of Read.
    • FileStream has the default 4K buffer size.


    Additionally the File class has an Open method which allows the specification of FileMode, FileAccess, and FileShare, similar to a FileStream constructor.

    1. Open - (specify read/write access) opens a FileStream with the specified mode, access, and sharing.
      FileStream myFileStream = File.Open(path, FileMode, FileAccess, FileShare);
      



    File and FileInfo Classes

    The File and FileInfo classes contain many of the same capabilities. An important difference between the two classes is that all the File class methods are static, while all the methods in the FileInfo class are instance methods. This makes a difference when accessing a file because each time a static File method is used, it performs a security check. When using an instance of FileInfo, the security check is only performed once. So as a general rule:

    "Use the File class when performing a single operation on a file. Use the FileInfo class when performing multiple operations on the same file."

    The following example program compares the coding of the File class with the FileInfo class for copying a file. If responding to the prompt to overwrite the existing file, then the File.Copy method is used with a Boolean flag set to true to allow the overwrite. If the file does not exist, then and instance of FileInfo is created and its CopyTo method is used to copy the file.

    Coping a File - File Class vs FileInfo Class

    using System;
    using System.IO;

    namespace FilevsFileInfo
    {
        class Program
        {
            static void Main()
            {
                String theInput;
                string fromPath = @"c:\kevin\From\myfile.txt";
                string toPath   = @"c:\kevin\To\myfile.txt";

                if (File.Exists(toPath))
                {
                    Console.WriteLine("File already exists: {0}", fromPath);
                    Console.Write("Do you wish to overwrite? (Y/N): ");
                    theInput = Console.ReadLine();

                    if (theInput.ToUpper() == "Y")                                        
                    {
                        // File Class Copy
                        File.Copy(fromPath, toPath, true);
                    }
                }
                else
                {
                    // FileInfo Class Copy
                    FileInfo myFileInfo = new FileInfo(fromPath);
                    myFileInfo.CopyTo(toPath);
                }
            }
        }
    }

    Below are additional static methods found in the File class and instance methods found in the FileInfo class. For the Getxxxx methods there are corresponding Setxxxx methods. Also for the Readxxxx methods there are corresponding Writexxxx methods.

    1. File - static methods for working with files.
      • Static Methods
        • AppendAllLines - appends lines to a file, and then closes the file. If the specified file does not exist, this method creates a file, writes the specified lines to the file, and then closes the file.
        • AppendText - creates a StreamWriter that appends UTF-8 encoded text to an existing file, or to a new file if the specified file does not exist.
        • CreateText - creates or opens a file for writing UTF-8 encoded text.
        • Decrypt - decrypts a file that was encrypted by the current account using the Encrypt method.
        • Encrypt - encrypts a file so that only the account used to encrypt the file can decrypt it.
        • Exists - determines whether the specified file exists.
        • GetAccessControl - gets a FileSecurity object that encapsulates the access control list (ACL) entries for a specified file.
        • GetAttributes - gets the FileAttributes of the file on the path.
        • ReadAllBytes - opens a binary file, reads the contents of the file into a byte array, and then closes the file.
        • ReadAllLines - opens a text file, reads all lines of the file, and then closes the file.
        • ReadAllText - opens a text file, reads all lines of the file, and then closes the file.
        • ReadLines - reads the lines of a file.
        • Replace - replaces the contents of a specified file with the contents of another file, deleting the original file, and creating a backup of the replaced file.

          The "ReadAll" and "WriteAll" methods will handle the file opens and closes automatically (even if an exception occurs). The following three lines of code copies a file and displays the file contents using just three lines.

          string fileContents = File.ReadAllText(@"c:\kevin\To\myFile.txt");
          File.WriteAllText(@"c:\kevin\To\myFile.new", fileContents);
           Console.WriteLine(fileContents);
          
    2. FileInfo - instance methods and properties for working with files.
      • Properties
        • Attributes - gets or sets the attributes for the current file or directory.
        • Directory - gets an instance of the parent directory.
        • DirectoryName - gets a string representing the directory's full path.
        • Exists - gets a value indicating whether a file exists.
        • Extension - gets the string representing the extension part of the file.
        • FullName - gets the full path of the directory or file.
        • IsReadOnly - gets or sets a value that determines if the current file is read only.
        • Length - gets the size, in bytes, of the current file.
      • Instance Methods
        • AppendText - creates a StreamWriter that appends text to the file represented by this instance of the FileInfo.
        • CopyTo(String, Boolean) - copies an existing file to a new file, allowing the overwriting of an existing file.
        • CreateText - creates a StreamWriter that writes a new text file.
        • Decrypt - decrypts a file that was encrypted by the current account using the Encrypt method.
        • Encrypt - encrypts a file so that only the account used to encrypt the file can decrypt it.
        • GetAccessControl() - gets a FileSecurity object that encapsulates the access control list (ACL) entries for the file described by the current FileInfo object.
        • MoveTo - moves a specified file to a new location, providing the option to specify a new file name.
        • Refresh - refreshes the state of the object.
        • Replace(String, String) - replaces the contents of a specified file with the file described by the current FileInfo object, deleting the original file, and creating a backup of the replaced file.



    Directory and Directory Info Classes

    The Directory and DirectoryInfo classes contain many of the same capabilities. An important difference between the two classes is that all the Directory class methods are static, while all the methods in the DirectoryInfo class are instance methods. This makes a difference when accessing a directory because each time a static Directory method is used, it performs a security check. When using an instance of DirectoryInfo, the security check is only performed once. So as a general rule:

    In .NET 4.0 the Enumeratexxxx methods where added. Unlike the corresponding Getxxxx methods, the Enumeratexxxx methods are lazily evaluated which make them work well with LINQ. MSDN describes the differences between theEnumeratexxxx methods and the Getxxxx methods:

    "When you use EnumerateDirectories, you can start enumerating the collection of names before the whole collection is returned; when you use GetDirectories, you must wait for the whole array of names to be returned before you can access the array. Therefore, when you are working with many files and directories, EnumerateDirectories can be more efficient."

    Below are additional static methods found in the Directory class and instance methods found in the DirectoryInfo class. For the Getxxxx methods there are corresponding Setxxxx methods. Also for the Readxxxx methods there are corresponding Writexxxx methods.

    1. Directory - static methods for working with directories.
      • Static Methods
        • CreateDirectory(String) - creates all directories and subdirectories in the specified path unless they already exist.
        • EnumerateDirectories(String) - returns an enumerable collection of directory names in a specified path.
        • EnumerateFiles(String) - returns an enumerable collection of file names in a specified path.
        • EnumerateFileSystemEntries(String) - returns an enumerable collection of file names and directory names that match a search pattern in a specified path.
        • Exists - determines whether the given path refers to an existing directory on disk.
        • GetAccessControl(String) - gets a DirectorySecurity object that encapsulates the specified type of access control list (ACL) entries for a specified directory.
        • GetDirectories(String) - returns the names of subdirectories (including their paths) that match the specified search pattern in the specified directory.
        • GetDirectoryRoot - returns the volume information, root information, or both for the specified path.
        • GetFiles(String) - returns the names of files (including their paths) in the specified directory.
        • GetFileSystemEntries(String) - returns the names of all files and subdirectories in a specified path.
        • GetLogicalDrives - retrieves the names of the logical drives on this computer in the form ":\".
        • Move - moves a file or a directory and its contents to a new location.
    2. DirectoryInfo - instance methods and properties for working with directories.
      • Properties
        • Attributes - gets or sets the attributes for the current file or directory.
        • Exists - gets a value indicating whether the directory exists.
        • Extension - gets the string representing the extension part of the file.
        • FullName - gets the full path of the directory or file.
        • Name - gets the name of this DirectoryInfo instance.
        • Parent - gets the parent directory of a specified subdirectory.
        • Root - gets the root portion of the directory.
      • Instance Methods
        • Create() - creates a directory.
        • CreateSubdirectory(String) - creates a subdirectory or subdirectories on the specified path.
        • EnumerateDirectories() - returns an enumerable collection of directory information that matches a specified search pattern
        • EnumerateFiles() - returns an enumerable collection of file information in the current directory.
        • EnumerateFileSystemInfos() - returns an enumerable collection of file information that matches a specified search pattern and search subdirectory option.
        • MoveTo - moves a DirectoryInfo instance and its contents to a new path.
        • Refresh - refreshes the state of the object.
        • SetAccessControl - applies access control list (ACL) entries described by a DirectorySecurity object to the directory described by the current DirectoryInfo object.

    The following example code shows how to obtain directory and file information with DirectoryInfo.EnumerateFileSystemInfo. Notice how Environment.SpecialFolder.Desktop is used to set the directory to the "desktop" folder. More information on Special Folders enumeration is given in the following sections.

    .DirectoryInfo.EnumerateFileSystemInfo



    Obtain Directory and File Information with DirectoryInfo.EnumerateFileSystemInfo

    using System;
    using System.IO;

    namespace EnumerateFileSystemInfoExample
    {
        class Program
        {
            static void Main()
            {
                var entries = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop)).EnumerateFileSystemInfos();
                foreach (var entry in entries)
                {
                    Console.WriteLine("Name: {0}", entry.FullName);
                    Console.WriteLine("  Attrib  : {0}", entry.Attributes);
                    Console.WriteLine("  Created : {0}", entry.CreationTime);
                    Console.WriteLine("  Accessed: {0}\n", entry.LastAccessTime);
                }           
            }
        }
    }

    Path Class

    The Path class performs operations on String instances that contain file or directory path information. For example the ChangeExtension method does not change the extension on the file, but changes the extension on a filename inside a string that contains the file path. You could then use the original path name and the newly created path name to rename the file using the File.Move method (see example program below).

    Below are some of the static methods found in the Path class

    1. Path - static methods for working with path strings.
      • Static Methods
        • ChangeExtension - changes the extension of a path string.
        • Combine(String[]) - combines an array of strings into a path.
        • GetDirectoryName - returns the directory information for the specified path string.
        • GetFileNameWithoutExtension - returns the file name of the specified path string without the extension.
        • GetInvalidFileNameChars - gets an array containing the characters that are not allowed in file names.
        • GetInvalidPathChars - gets an array containing the characters that are not allowed in path names.
        • GetRandomFileName - returns a random folder name or file name.
        • HasExtension - determines whether a path includes a file name extension.
        • IsPathRooted - gets a value indicating whether the specified path string contains a root.

    The following example code shows how use Path.ChangeExtension to change the file extension inside a string which contains the full path name. The modified string is then used by the File.Move method to rename the file.

    Change File Extension in Path String

    using System;
    using System.IO;

    namespace PathExample
    {
        class Program
        {
            static void Main()
            {
                string newFileName;

                // Get New File Name with Extension Changed
                string myFileName = @"C:\kevin\To\myfile.txt";
                newFileName = Path.ChangeExtension(myFileName, ".hold");

                // Rename the File
                try
                {
                    File.Move(myFileName, newFileName);
                }
                catch (Exception e)
                {
                    System.Console.WriteLine(e.Message);
                }
            }
        }
    }

    Special Folders Enumeration

    The Environment.SpecialFolder Enumeration contains constants used to retrieve directory paths to system special folders. A few of these special folder enumerations are listed below with the entire enumeration and corresponding paths displayed from the program.

    • Desktop - the logical Desktop rather than the physical file system location.
    • Favorites - the directory that serves as a common repository for the user's favorite items.
    • MyComputer - the My Computer folder. The MyComputer constant always yields the empty string ("") because no path is defined for the My Computer folder.
    • MyMusic - the My Music folder.
    • MyDocuments - the My Documents folder. This member is equivalent to Personal.

    The following example code shows how to obtain and use the information inside the Environment.SpecialFolder enumeration. The program lists the corresponding actual paths on my computer for the corresponding SpecialFolder constant.

    .Special Folder Enumeration and Paths



    Special Folder Enumeration and Paths

    using System;
    using System.Collections.Generic;

    namespace SpecialFoldersExample
    {
        class Program
        {
            static void Main()
            {

                // Print out Enumeration Values and Constants and Corresponding Paths
                foreach (var specialFolder in Enum.GetValues(typeof(Environment.SpecialFolder)))
                    Console.WriteLine("{0,2} {1,22} {2}", (int)specialFolder,
                        ((Environment.SpecialFolder)specialFolder),
                        Environment.GetFolderPath(((Environment.SpecialFolder)specialFolder)));
            }
        }
    }



    DriveInfo Class

    The DriveInfo class provides information about the accessible computer drives. DriveInfo contains methods and properties used to query for drive information. DriveInfo can be used to determine what drives are available, drive types, capacity and free space.

    1. DriveInfo - class contains methods and properties used to query for drive information.
      • Properties
        • AvailableFreeSpace - indicates the amount of available free space on a drive, in bytes
        • DriveFormat - gets the name of the file system, such as NTFS or FAT32.
        • DriveType - gets the drive type, such as CD-ROM, removable, network, or fixed.
        • IsReady - gets a value that indicates whether a drive is ready.
        • Name - gets the name of a drive, such as C:\.
        • RootDirectory - gets the root directory of a drive.
        • TotalFreeSpace - gets the total amount of free space available on a drive, in bytes.
        • TotalSize - gets the total size of storage space on a drive, in bytes.
        • VolumeLabel - gets or sets the volume label of a drive.
      • Static Methods
        • GetDrives - retrieves the drive names of all logical drives on a computer.

    The following program show how to use the DriveInfo class to scan all the drives on a computer and report the drive information. If no media is in the removable drives, then only the drive name and type will be reported.

    .DriveInfo Class



    DriveInfo Class Reporting Drive Information

    using System;
    using System.IO;

    class DriveInfoExample
    {
        public static void Main()
        {
            DriveInfo[] computerDrives = DriveInfo.GetDrives();

            Console.WriteLine("Computer : {0}", System.Environment.MachineName);
            Console.WriteLine("Run Date : {0}", DateTime.Now);
            Console.WriteLine("----------------------------------\n");

            foreach (DriveInfo drive in computerDrives)
            {
                Console.WriteLine("Drive {0} :", drive.Name);
                Console.WriteLine("    File type: {0}", drive.DriveType);
                if (drive.IsReady == true)
                {
                    Console.WriteLine("    Volume label    : {0}", drive.VolumeLabel);
                    Console.WriteLine("    File system     : {0}", drive.DriveFormat);
                    Console.WriteLine("    Total Size      : {0,15:N0} bytes ", drive.TotalSize);
                    Console.WriteLine("    Free Space      : {0,15:N0} bytes", drive.AvailableFreeSpace);
                    Console.WriteLine("    Total Free Space: {0,15:N0} bytes", drive.TotalFreeSpace);
                }
                Console.WriteLine();
            }
        }
    }

    Top




    Memory Stream

    The entire backing store (an array) for a MemoryStream must reside entirely in memory. This can be useful when you need random access to a non-seekable stream. While a FileStream allows random access, it is slow as it is optimized for sequential access. MemoryStream, or introduced in .NET 4.0 Memory-Mapped files can be used to optimize random access.

    A memory stream is easy to convert to a byte array by using the ToArray() method. Memory streams may also be useful when compressing or encrypting data. The .NET framework also contains an UnmanagedMemoryStream which provides access to unmanaged blocks of memory from managed code.

    The following example program uses a memory stream to displays the middle twenty characters from a file. I first reads the bytes from a file into an array. It then writes the byte array to a memory stream. The middle twenty bytes from the memory stream is written to a second byte array, which is encoded and displayed as text. The entire array of bytes from the file is also displayed for comparison.

    .Memory Stream Usage



    Memory Stream Used to Extract Bytes from File

    using System;
    using System.IO;
    using System.Linq;

    namespace MemoryStreamExample
    {
        class Program
        {       
            static void Main()
            {
                const int NUM_BYTES_TO_WRITE = 20;

                byte[] byteArray; // Bytes from File
                byte[] middleBytes = Enumerable.Repeat((byte)0x20, NUM_BYTES_TO_WRITE).ToArray();

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    try
                    {
                        Console.WriteLine("--------------------------------------");
                        Console.WriteLine("Display Middle Twenty Bytes from File.");
                        Console.WriteLine("--------------------------------------\n");

                        // Read Bytes from File into Byte Array
                        byteArray = File.ReadAllBytes(@"C:\kevin\From\myFile.txt");
                       
                        // Write Bytes from File to Memory Stream
                        memoryStream.Write(byteArray, 0, byteArray.Length);

                        // Determine Point to Start Getting Bytes
                        long startPoint = memoryStream.Length / 2 - (NUM_BYTES_TO_WRITE / 2);

                        // Move to Point to Start Getting Bytes
                        memoryStream.Seek(startPoint, SeekOrigin.Begin);

                        // Read Bytes from Memory Stream to Byte Array
                        memoryStream.Read(middleBytes, 0, NUM_BYTES_TO_WRITE);

                        // Display Middle Bytes from Byte Array   
                        Console.WriteLine("--------------------------------------");
                        Console.WriteLine("----------- Middle Bytes -------------");
                        Console.WriteLine("--------------------------------------");
                        Console.WriteLine(System.Text.Encoding.Default.GetString(middleBytes));
                        Console.WriteLine("\n");

                        // Display Entire Byte Array Read from File
                        Console.WriteLine("--------------------------------------");
                        Console.WriteLine("----------- Entire File --------------");
                        Console.WriteLine("--------------------------------------");
                        Console.WriteLine(System.Text.Encoding.Default.GetString(byteArray));
                    }
                    catch (Exception e)
                    {
                        System.Console.WriteLine(e.Message);
                    }               
                }           
            }
        }
    }

    Top




    Memory-Mapped Files

    Memory-Mapped Files where introduced in .NET 4.0. A memory-mapped files use managed coded and contain the contents of files in virtual memory. Memory-mapped files are classified into two categories:

    1. Persisted - are memory-mapped files that are associated with a source file on a disk. When the last process has finished working with the file, the data is saved to the source file on the disk. These memory-mapped files are suitable for working with extremely large source files.

    2. Non-Persisited - are memory-mapped files that are not associated with a file on a disk. When the last process has finished working with the file, the data is lost and the file is reclaimed by garbage collection. These files are suitable for creating shared memory for inter-process communications (IPC).
    Using Memory-Mapped Files for Random File Access

    One reason to use memory-mapped files is for efficient random access of files. Memory-mapped files are several times faster than FileStreams for random access (However FileStreams are several times faster than memory-mapped files for sequential access). MemoryMappedViewAccessors are used with memory-mapped files to provide a randomly accessed view. Memory mapped view accessors provide methods for randomly reading and writing value types (primitives, structures, etc.), but does not support reference types (strings, classes, etc.). The public static members of MemoryMappedViewAccessors are thread safe, while any instance methods are not guaranteed to be thread safe.

    Using Memory-Mapped Files to Share Memory Between Processes

    Memory-Mapped files can be shared across multiple processes which are running on the same computer. The memory-mapped file is given a name by the process which creates it (using theMemoryMappedFile.CreateNew method) , then other processes can access it by name. The following programs show how a memory-mapped file can be shared between two different processes. The memory-mapped file is defined as 100 bytes in size with a name of "SharedMMF".

    .Memory-Mapped File Shared Between Processes



    Memory-Mapped File Shared Between Processes

    Process 1 - Create Shared Memory-Mapped File

    using System;
    using System.IO.MemoryMappedFiles;

    namespace MemoryMappedFileIPC
    {
        class Program
        {
            static void Main()
            {
                Console.WriteLine("Process 1");
                Console.WriteLine("----------\n");

                // Create and Populate Shared MemoryMapped File
                using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("SharedMMF", 100))
                using (MemoryMappedViewAccessor mmva = mmf.CreateViewAccessor())
                {
                    mmva.Write(0, 747);
                    Console.WriteLine("SharedMMF is alive");
                    Console.ReadLine(); // Keep Shared Memory Alive
                }
            }
        }
    }

    Process 2 - Read Shared Memory-Mapped File

    using System;
    using System.IO.MemoryMappedFiles;

    namespace MemoryMappedFileIPC
    {
        class Program
        {
            static void Main()
            {
                Console.WriteLine("Process 2");
                Console.WriteLine("----------\n");

                // Read Data from Shared MemoryMapped File
                using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("SharedMMF"))
                using (MemoryMappedViewAccessor mmva = mmf.CreateViewAccessor())
                {
                    Console.WriteLine("Read from SharedMMF: {0}\n",mmva.ReadInt32(0));               
                }
            }
        }
    }

    Top




    Pipe Stream



    "The PipeStream class was introduced in .NET 3.5 and provides the base class for support of Anonymous and Named pipes (Windows protocol)."

    Pipes are a simple and efficient mechanism for allowing processes to communicate. Processes could open a TCP port to pass data, but this adds a lot of overhead from the network stack. Using pipes, the data is passed straight through the kernel between processes and avoids the network stack overhead. Pipes allow one process to write data to a pipe, then the other process(es) wait for the data and read it from the pipe. The concept of pipes is attributed to Douglas McIlroy. Ken Thompson added pipes to Unix in 1973. Pipes were adopted by other operating systems and became known as the Pipes and Filters design pattern. Pipe protocols and usage vary among operating systems.

    Pipes provide one of the simplest means for Interprocess Communication (IPC). Other types of IPC include:

    1. Signals - software-generated interrupts sent to a process when an event occurs.
    2. Messaging - processes to exchange data by using a message queue.
    3. Semaphores - synchronizes processes competing for the same resource.
    4. Shared Memory - memory segments are shared among processes.
    5. Sockets - end-points for communication between processes.

    The PipeStream class was introduced in .NET 3.5 and provides the base class for support of Anonymous and Named pipes (Windows protocol). The types for providing the support for pipes are located in the System.IO.Pipes Namespace. Two good references for the .NET usage of pipes are Pipes from Windows Dev Center and Pipe Operations in the .NET Framework from Microsoft Developer Network.



    Anonymous Pipes vs Named Pipes

    Anonymous pipes require less overhead than named pipes, but have limited functionality. Differences between anonymous and named pipes include:

    1. Anonymous Pipes - .a.k.a "Unnamed Pipes" in Unix terminology.
      • Anonymous pipes are one-way pipes that typically transfer data between parent and child processes.
      • Anonymous pipes are always local; they cannot be used over a network.
      • Anonymous pipes do not support Message read modes.
    1. Named Pipes
      • Named pipes provide communication between a pipe server process and one or more pipe client processes.
      • Named pipes can be used for IPC communication locally or over a network.
      • Named pipes support either one-way or two-way (full duplex) communications.
      • Named pipes support message-based communication and allow multiple clients to connect simultaneously to the server process.
      • Named pipes support impersonation, which enables connecting processes to use their own permissions on remote servers.
    Anonymous Pipes

    Anonymous pipes are implemented with the AnonymousPipeServerStream and AnonymousPipeClientStream classes. The client side of an anonymous pipe must be created from a pipe handle provided by the server side by calling the GetClientHandleAsString method. The string is then passed as a parameter when creating the client process. From the client process, it is passed to the AnonymousPipeClientStream constructor as the pipeHandleAsString> parameter.

    Anonymous Pipe Classes:

    1. AnonymousPipeServerStream
      • Exposes a stream around an anonymous pipe, which supports both synchronous and asynchronous read and write operations.
      • The AnonymousPipeServerStream object must dispose the client handle using the DisposeLocalCopyOfClientHandle method in order to be notified when the client exits.
    2. AnonymousPipeClientStream
      • Exposes the client side of an anonymous pipe stream, which supports both synchronous and asynchronous read and write operations.
      • The client side of an anonymous pipe must be created from a pipe handle provided by the server side by calling the GetClientHandleAsString method.
    Named Pipes

    Named pipes are implemented with the NamedPipeServerStream and NamedPipeClientStream classes. Named pipes can be used for interprocess communication locally or over a network. A single pipe name can be shared by multiple NamedPipeClientStream objects. When the client and server processes are run on the same computer, the server name provided to the NamedPipeClientStream object can be ".". However if the client and server processes are on separate computers, the server name provided to the NamedPipeClientStream object would be the network name of the computer that runs the server process. Any process can act as either a named pipe server or client, or both.

    Named Pipe Classes:

    1. NamedPipeServerStream
      • Exposes a Stream around a named pipe server, supporting both synchronous and asynchronous read and write operations
      • A single pipe name can be shared by multiple NamedPipeClientStream objects.
    2. NamedPipeClientStream
      • Exposes a Stream around a named pipe client, which supports both synchronous and asynchronous read and write operations.

    The following program uses named pipes to accept a value from the keyboard (by client) and write it to the pipe. The server reads the value from the pipe, adds angle brackets around it, then writes it back to the pipe. The client then reads the modified version of the value from the pipe. Both the client and the server continue to run until a null value is entered from the keyboard.

    .Named Pipes



    Named Pipes Example

    using System;
    using System.IO;
    using System.IO.Pipes;
    using System.Threading.Tasks;

    namespace NamedPipesExample
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("-----------------------------------------------");
                Console.WriteLine("------------- Named Pipes Example -------------");
                Console.WriteLine("-----------------------------------------------");
                Console.WriteLine(" 1. CW - Client Writes keyboard entry to pipe  ");
                Console.WriteLine(" 2. SR - Server Reads from pipe                ");
                Console.WriteLine(" 3. Server adds brackets to value              ");
                Console.WriteLine(" 4. SW - Server Writes to pipe                 ");
                Console.WriteLine(" 5. CR - Client Reads from pipe                ");
                Console.WriteLine("-----------------------------------------------\n");

                StartServer();

                // Client Stream
                var client = new NamedPipeClientStream("PipeDreams");
                client.Connect();
                StreamReader reader = new StreamReader(client);
                StreamWriter writer = new StreamWriter(client);

                while (true)
                {
                    string input = Console.ReadLine();
                    if (String.IsNullOrEmpty(input)) break;
                    // Write to Pipe
                    writer.WriteLine("CW: {0}", input);
                    writer.Flush();
                    // Read from Pipe
                    Console.WriteLine("CR: {0}\n", reader.ReadLine());
                }
            }

            static void StartServer()
            {
                string line = null;

                Task.Factory.StartNew(() =>
                {
                    var server = new NamedPipeServerStream("PipeDreams");
                    server.WaitForConnection();
                    StreamReader reader = new StreamReader(server);
                    StreamWriter writer = new StreamWriter(server);
                    while (true)
                    {
                        // Read from Pipe
                        line = "SR: " + reader.ReadLine();
                        line = "<" + line + ">";
                        // Write to Pipe
                        writer.WriteLine("SW: {0}", line);
                        writer.Flush();
                    }
                });
            }
        }
    }

    Top




    Buffered Stream

    A BufferedStream is a buffer over an existing stream. Buffers can improve performance by reducing the number of I/O operations on expensive resources. A buffer is a block of bytes in memory used to cache data. BufferStreams are decorator streams which add functionality to an existing stream. This is not to be confused with a MemoryStream which is a backing store stream (the source of the entire stream data).

    The BufferedStream class must be configured to either read or write, but the BufferedStream cannot be configured to perform both the tasks at the same time. If you always read and write for sizes greater than the internal buffer size, then BufferedStream might not even allocate the internal buffer. BufferedStream also buffers reads and writes in a shared buffer. Closing a BufferStream automatically closes the underlying backing store stream.

    Note: Microsoft improved the performance of all streams in the .NET Framework by including a built-in buffer (around 2006). Applying a BufferedStream to an existing .NET Framework stream results in a double buffer. The BufferedStream is most commonly used in custom stream classes that do not include a built-in buffer.

    Top




    Text Adapters



    "StreamReader/StreamWriter uses a byte stream for its backing data store and must translate bytes to characters. StringReader/StringWriter uses a string or StringBuilder for its backing data store so no character translation is needed. Both of these stream and string adapters can only read/write character based data."

    Text adapters read/write only character-based data (i.e. string and char only, no other data types). The TextReader and TextWriter classes are the abstract base classes for text adapters. The Text base classes are used to derive concrete classes which are used as stream and string adapters:

    1. StreamReader and StreamWriter
      • Uses a byte stream for its backing data store.
      • Requires translation from bytes to characters.
      • Will throw an exception if it encounters bytes which do not have a valid string translation.
    2. StringReader and StringWriter
      • Uses a string or StringBuilder for its backing data store.
      • Requires no byte/character translation.
    StreamReader/StreamWriter Classes

    The StreamReader/StreamWriter classes were designed for reading/writing character data such as a standard text file. They default to UTF-8 encoding which correctly handles Unicode characters. To be thread safe, the TextReader.Synchronized must be used. The Close() and Dipose() methods are synonymous for adapters, just as they are for streams. When you close an adapter, you also close the underlying stream.

    1. StreamReader - implements a TextReader that reads characters from a byte stream in a particular encoding .
      • Properties
        • BaseStream - returns the underlying stream.
        • CurrentEncoding - gets the current character encoding that the current StreamReader object is using.
        • EndOfStream - gets a value that indicates whether the current stream position is at the end of the stream.
      • Methods
        • DiscardBufferedData - Clears the internal buffer.
        • Finalize - allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        • Peek - returns the next available character but does not consume it.
        • Read() - reads the next character from the input stream and advances the character position by one character.
        • ReadAsync - reads a specified maximum number of characters from the current stream asynchronously and writes the data to a buffer, beginning at the specified index.
        • ReadBlock - reads a specified maximum number of characters from the current stream and writes the data to a buffer, beginning at the specified index.
        • ReadLine - reads a line of characters from the current stream and returns the data as a string.
        • ReadToEnd - reads all characters from the current position to the end of the stream.
    2. StreamWriter - implements a TextWriter for writing characters to a stream in a particular encoding .
      • Properties
        • AutoFlush - gets or sets a value indicating whether the StreamWriter will flush its buffer to the underlying stream after every call to StreamWriter.Write.
        • BaseStream - gets the underlying stream that interfaces with a backing store.
        • Encoding - gets the Encoding in which the output is written.
        • FormatProvider - gets an object that controls formatting.
        • NewLine - gets or sets the line terminator string used by the current TextWriter.
      • Methods
        • Finalize - allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        • Flush - clears all buffers for the current writer and causes any buffered data to be written to the underlying stream. .
        • MemberwiseClone - creates a shallow copy of the current Object.
        • Write(type) - writes the text representation of the type.
        • WriteLine - writes a line terminator to the text string or stream.
        • WriteAsync(type) - writes the text representation of the type to the stream asynchronously.

    Translation for StreamReader/StreamWriter

    A stream is an abstraction for a sequence of bytes. To use the bytes they usually need to be translated to a particular type of encoding, such as Unicode, ASCII, or UTF-8. The Encoding and Decoding classes are used to the translation between raw bytes and encoded values, or between different encoded values (e.g. ASCII to Unicode). For more information about character encoding, see the MSDN article Character Encoding in the .NET Framework.

    StringReader/StringWriter Classes

    The StringReader/StringWriter classes were designed for reading/writing string data which is already in character format. So no byte/character translation is needed. These classes are used when dealing with several string manipulations

    1. StringReader - implements a TextReader that reads from a string or StringBuilder.
      • Methods
        • Finalize - allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        • MemberwiseClone() - creates a shallow copy of the current MarshalByRefObject object.
        • Peek - returns the next available character but does not consume it.
        • Read - reads the next character from the input string and advances the character position by one character.
        • ReadAsync - reads a specified maximum number of characters from the current string asynchronously and writes the data to a buffer, beginning at the specified index.
        • ReadBlock - reads a specified maximum number of characters from the current text reader and writes the data to a buffer, beginning at the specified index.
        • ReadToEnd - reads all characters from the current position to the end of the string and returns them as a single string.
    2. StringWriter - implements a TextWriter for writing information to a string. The information is stored in an underlying StringBuilder. .
      • Methods
        • Finalize - allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        • Flush - clears all buffers for the current writer and causes any buffered data to be written to the underlying device.
        • Write(type) - write the data as the specified type to the string (or stream).
        • WriteLine - writes a line terminator to the text string (or stream).
        • ReadBlock - reads a specified maximum number of characters from the current text reader and writes the data to a buffer, beginning at the specified index.
        • ReadToEnd - reads all characters from the current position to the end of the string and returns them as a single string.
    StreamReader/StreamWriter Read and Writing of Text Files

    The following program uses a StreamReader to read all the text files in the user's My Documents folder. It then uses a StreamWriter to write all the contents of all the individual text files into one large text file called "AllTxtFiles.txt".

    using System;
    using System.IO;
    using System.Text;

    class Program
    {
        static void Main()
        {
            string mydocpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            StringBuilder sb = new StringBuilder();

            foreach (string txtName in Directory.EnumerateFiles(mydocpath, "*.txt"))
            {
                using (StreamReader sr = new StreamReader(txtName))
                {
                    sb.AppendLine("----------------------------------------------");
                    sb.AppendLine(txtName.ToString()); // File Name
                    sb.AppendLine("----------------------------------------------");
                    sb.Append(sr.ReadToEnd()); // File Contents
                    sb.AppendLine();
                    sb.AppendLine();
                }
            }

            using (StreamWriter outfile = new StreamWriter(mydocpath + @"\AllTxtFiles.txt"))
            {
                outfile.Write(sb.ToString());
            }
        }
    }

    Top




    Binary Adapters

    "Binary adapters can process all the primitive data types, where the Text adapters are restricting to processing only character data."

    Binary adapters read/write all the primitive data types (e.g. char, int, float, byte, bool, etc.) plus strings and arrays of primitive data types. The BinaryReader and BinaryWriter classes contains several methods for reading and writing which specify the type of data, such as ReadInt32, ReadInt64, ReadDouble, ReadBoolean, ReadByte, etc.

    The underlying data stream is specified when you create an instance of the BinaryReader. You can also optionally specify the type of encoding and whether to leave the stream open after disposing the BinaryReader object. If you do not specify an encoding type, UTF-8 is used.

    The following example uses a BinaryWriter to create and write binary data to a file. It then uses the BinaryReader to open the file and display the binary data.

    .Binary Adapter to Create Binary File



    BinaryReader/Writer to Create and Read Binary File

    using System;
    using System.IO;

    namespace BinaryFileExample
    {
        class Program
        {
            static void Main()
            {
                Console.WriteLine("-------------------------------------------");
                Console.WriteLine("-------- Write Binary Data to File --------");
                Console.WriteLine("-------------------------------------------");

                // Data to Write to File
                int i = 32;
                double d = 3.14157;
                bool b = true;
                char c = 'A';           
                string s = "Is it Spring Yet!";           

                // Create and Write Values to Binary File
                using (BinaryWriter writer = new BinaryWriter(new FileStream(@"c:\kevin\From\bindata", FileMode.Create)))
                {
                    writer.Write(i);
                    writer.Write(d);
                    writer.Write(b);
                    writer.Write(c);
                    writer.Write(s);
                }

                // Open Binary File for Reading
                using (BinaryReader reader = new BinaryReader(new FileStream(@"c:\kevin\From\bindata", FileMode.Open)))
                {
                    i = reader.ReadInt32();
                    Console.WriteLine("Integer data: {0}", i);
                    d = reader.ReadDouble();
                    Console.WriteLine("Double data: {0}", d);
                    b = reader.ReadBoolean();
                    Console.WriteLine("Boolean data: {0}", b);
                    c = reader.ReadChar();
                    Console.WriteLine("Character data: {0}\n", c);
                    s = reader.ReadString();
                    Console.WriteLine("String data: {0}\n", s);           
                }
            }
        }
    }

    Top




    FileSystemWatcher Class

    The FileSystemWatcher watches for specific changes in a file system and allows actions to be performed when a change is detected. FileSystemWatcher can be used to watch for changes in files and subdirectories of the specified directory. It can be used to watch files on a local computer, a network drive, or a remote computer.

    There are several types of changes you can watch for in a directory or file, such as changes in: attributes, file time stamps, file and directory sizes, renaming, deletion, or creation of files or directories. To watch for changes in all files, set the Filter property to an empty string ("") or use wildcards ("*.*"). To watch a specific file, set the Filter property to the file name. For example, to watch for changes in text files, set the Filter property to "*.txt".

    The Windows operating system notifies your component of file changes in a buffer created by the FileSystemWatcher. If there are many changes in a short time, the buffer can overflow. This causes the component to lose track of changes in the directory, and it will only provide blanket notification. Increasing the size of the buffer with the InternalBufferSize property is expensive, as it comes from non-paged memory that cannot be swapped out to disk, so keep the buffer as small yet large enough to not miss any file change events. To avoid a buffer overflow, use the NotifyFilter and IncludeSubdirectories properties so you can filter out unwanted change notifications.

    Note the following when using the FileSystemWatcher class:

    1. Hidden files are not ignored.
    2. In some systems, FileSystemWatcher reports changes to files using the short 8.3 file name format. For example, a change to "LongFileName.LongExtension" could be reported as "LongFil~.Lon".
    3. This class contains a link demand and an inheritance demand at the class level that applies to all members. A SecurityException is thrown when either the immediate caller or the derived class does not have full-trust permission. For details about security demands, see Link Demands.
    4. The maximum size you can set for the InternalBufferSize property for monitoring a directory over the network is 64 KB.

    FileSystemWatcher notifies a process about changes to the file system by raising events. Several factors can affect which events are raised. Some common file system operations may raise more than one event as some complex operations consist of multiple simple operations. The following events can be raised:

    1. Changed - occurs when a file or directory in the specified Path is changed.
    2. Created - occurs when a file or directory in the specified Path is created.
    3. Deleted - occurs when a file or directory in the specified Path is deleted.
    4. Disposed - occurs when the component is disposed by a call to the Dispose method.
    5. Error - occurs when the instance of FileSystemWatcher is unable to continue monitoring changes or when the internal buffer overflows.
    6. Renamed - occurs when a file or directory in the specified Path is renamed.
    Top




    Isolated Storage

    Isolated Storage is an obfuscated storage area containing files and directories. Isolated stores are usually scoped by user and assembly, so most other managed code will not be able to access the application's data. However, highly trusted managed code, unmanaged code, and administration tools can access any isolated storage area. Further, isolated storage is not encrypted and can be transient as the user can delete isolated storage. For additional details on Isolated Storage see my Silverlight page Local File Access.

    Top



    Reference Articles

    Top