Input and Output

Lesson 1: File System Navigation

System.IO namespace = set of classes to navigate + manipulate files, directories and drives. Separated into two types - informational and utility.

Most informational classes derived from FileSystemInfo. Expose info about file system objects - files (FileInfo) and directories (DirectoryInfo). The DriveInfo class represents drives, but is not derived from FileSystemInfo as it does not share common behaviour with other informational classes - e.g. Can't delete drives.

Utility classes provide static methods to perform operations on file system objects. Include File, Directoryand Pathclasses.

Obtain information on file

  1. Create FileInfo object using path to file
  2. Access FileInfo properties

e.g.

FileInfo aFile = new FileInfo(@"C:\boot.ini");

if (aFile.Exists)
{
    Console.WriteLine(aFile.FullName);
}

Copy a file

FileInfo allows operations to be performed on file, e.g.

FileInfo aFile = new FileInfo(@"C:\boot.ini");

if (aFile.Exists)
{
    aFile.CopyTo(@"C:\boot.bak");
}

Enumerate file in directory

  1. Create DirectoryInfo object using path to directory
  2. Call GetFiles on the DirectoryInfo object

e.g.

DirectoryInfo aDirectory = new DirectoryInfo(@"C:\windows");

foreach(FileInfo file in aDirectory.GetFiles())
{
    Console.WriteLine(file.FullName);
}

Enumerating Drives

Call GetDrives method on DriveInfo class

e.g.

foreach(DriveInfo drive in DriveInfo.GetDrives())
{
    Console.WriteLine(derive.DriveType);
}

Changing file extension in a path

Use the Pathclass...

string aPath = @"C:\boot.ini";

Console.WriteLine(Path.ChangeExtension(aPath), "bak");

Monitoring Directory for changes

  1. Create FileSystemWatcher object and specify path to monitor
  2. Register for Created and Deleted events
  3. Turn on events

e.g.

FileSystemWatcher watcher = new FileSystemWatcher();

watcher.Path = @"C:\";

watcher.Created += new FileSystemEventHandler(watcher_Changed);

watcher.Deleted += new FileSystemEventHandler(watcher_Changed);

watcher.EnableRaisingEvents = true;

Other events are available, such as Changedand Renamed

While watching file system can get more events than FileSystemWatchercan handle. When too many events occur the object raises the Errorevent. Clients should register for the Errorevent and take appropriate action.

Lesson 2: Reading / Writing Files

Streams

Deal with both sequential and random access data

Derived from abstract class - specifies interface + provides basic implementation. Includes properties such as CanRead, CanSeek, CanTimeout, Position, etc. and methods like Close, Flush, Read, Write, Seek. Use common base class as working with data as a flow is common requirement, e.g.

static void DumpStream(Stream theStream)
{
    theStream.Position = 0;

    while (theStream.Position != theStream.Length)
    {
        Console.Write("{0:x2}, theStream.ReadByte());
    }
}

Derived streams include FileStream, MemoryStream, CryptoStream, NetworkStream, GzipStream.

File Class

Basic functionality to open file streams. Important static methods include:

AppendText - append string to a file

Copy - copies a file to a new file

Create - creates or opens a file returning a StreamWriter

Move - move a file from one place to another

ReadAllText - reads contents of file into string

WriteAllBytes - writes contents of byte array to file

Directory Class

Provides static methods for manipulating and creating directories. Methods include

CreateDirectory - Creates all directories in supplied path

Delete - Deletes specified directory

GetFiles - Names of files in directory

Move - Move file or directory (and its contents) to specified location

Reading from file

Most simple form - use File class to open stream, e.g.

FileStream theFile = File.Open(@"C:\boot.ini", FileMode.Open, FileAccess.Read);

Can view contents using the base class methods Read or ReadByte. Better access facilities provided by StreamReader and StreamWriter classes, e.g.

StreamReader rdr = new StreamReader(theFile);

Console.Write(rdr.ReadToEnd());

rdr.Close();

theFile.Close();

StreamReader reads stream as string, not as series of bytes. All its methods return strings or arrays of strings.

File class supports creating StreamReader directly via the OpenText method, e.g.

StreamReader rdr = File.OpenText(@"C:\boot.ini");

Console.Write(rdr.ReadToEnd());

rdr.Close();

Can read file in single call, e.g.

Console.WriteLine(File.ReadAllText(@"C:\boot.ini"));

Writing to a file

Before writing to a file it must exist - use File.Create() to achieve this.

Using FileStream returned by File.Create can write directly using Write and WriteByte(). Usually use StreamWriter to write to file. As with reading text the File class exposes CreateText method that returns StreamWriter e.g.:

StreamWriter writer = File.CreateText(@"C:\somefile.txt");

writer.WriteLine("Hello");

writer.Close();

File class supports WriteAllText method that creates (optionally) and writes contents of string to file in one operation, e.g.

File.WriteAllText(@"C:\somefile.txt", "Hello");

File.Open method, e.g.

FileeStream theFile = File.Open(@"C:\somefile.txt", FileMode.Open, FileAccess.Write);

Can achieve equivalent using the File.OpenWrite method that takes only 1 argument - the file name.

To accommodate situations where the file to be written to may or may not exist change the FileMode in the File.Open method to FileMode.OpenOrCreate

Understanding Readers and Writers

StreamReader and StreamWriter classes derive from abstract class TextReader and TextWriter.

Other classes also derived from TextReader and TextWriter, e.g. StringReader and StringWriter which are streams that target strings, e.g.

string s = @"Hello all

This is a multi-line

text string";

StringReader rdr = new StringReader(s);

// while there are more characters...
while (rdr.Peek() != -1)
{
  Console.WriteLine(rdr.ReadLine());
}

The BinaryReader namespace supports two classes (BinaryReader and BinaryWriter) for reading / writing binary data. Provides methods to read / write various data types, e.g.

FileStream newFile = File.Create(@"C:\somefile.bin");

BinaryWriter writer = new BinaryWriter(newFile);

writer.Write(100L);

writer.Write(new byte[] {10, 20, 50 100});

writer.Writer("hunger");

For every BinaryWriter.Write call must call corresponding BinaryReader.Read, e.g.

FileStream aFile = File.Open(@"c:\somefile.bin", FileMode.Open);

BinaryReader reader = new BinaryReader(aFile);

long number = reader.ReadInt64();

byte[] bytes = reader.ReadBytes(4);

string s = reader.ReadString();

Memory Stream

Creates streams in memory - useful to store data into before storing it (for example to a file).

Can use StreamWriters, etc just as with FileStream, e.g.

MemoryStream mem = new MemoryStream();

StreamWriter writer = new StreamWriter(mem);

writer.WriteLine("Hello");

Use of memory streams is usually a temporary measure. Class supports writing directly to another stream or copying data to other storage. Common use of class is to limit the time a file is held open (and thus locked to others), e.g.:

MemoryStream memStr = new MemoryStream();

StreamWriter writer = new StreamWriter(memStr);

writer.WriteLine("Hello");

writer.WriteLine("Goodbye");

// Force writer to flush data to stream

writer.Flush();

// Create file stream

FileStream theFile = File.Create(@"C:\inmemory.txt");

// Write memory stream to file

mem.WriteTo(theFile);

// Clean up

writer.Close();

theFile.Close();

mem.Close();

Buffered Stream

Writing to stream can be convenient, but performance is constrained by destination media. Buffered stream wraps another stream and only allows writes to happen when buffer is flushed, e.g.:

FileStream newFile = File.Create(@"c:\test.txt");

BufferedStream buffered = new BufferedStream(newFile);

StreamWriter writer = new StreamWriter(buffered);

writer.WriteLine("Some data");

// Write the data to the file...

writer.Close(); 

Lesson 3: Compressing Streams

Two methods for compressing data - GZIP and DEFLATE. Use same algorithm, but GZIP includes extra header information that allows its results to be decoded via widely used gzip tool. Use GZIP if resulting data is to be decoded by others, otherwise use DEFLATE as it generates slightly smaller files (no header information).

Compress / decompress any data up to 4GB in size.

Compress data with Compression Stream

Different to previous streams. Instead of writing to resource, it targets another stream such as a FileStream or MemoryStream.

// Open file to be compressed and file to write to
FileStream sourceFile = File.OpenRead(inFileName);

FileStream destFile = File.Create(outFileName);

// Wrap the destination stream in a compression stream
GzipStream compStream = new GzipStream(destFile, CompressionMode.Compress);

// Process the source file
int theByte = sourceFile.ReadByte();

while (theByte != -1)
{
  compStream.WriteByte((byte)theByte);

  theByte = sourceFile.ReadByte();  
}

Note, do not write to destination stream at all. Writing to the Compress Stream results in data going to destination stream.

Decompression is similar...

// Open file to be decompressed and file to write to
FileStream sourceFile = File.OpenRead(inFileName);

FileStream destFile = File.Create(outFileName);

// Wrap the destination stream in a compression stream
GzipStream compStream = new GzipStream(sourceFile, CompressionMode.Decompress);

// Process the source file
int theByte = compStream.ReadByte();

while (theByte != -1)
{
  destFile.WriteByte((byte)theByte);

  theByte = compStream.ReadByte();  
}

Lesson 4: Isolated Storage

Unfettered access to computer = bad idea.

Working in sandbox of limited security = good

Programs need to store state data.

To bridge these requirements use isolated storage which provides location to store persistent data without having to perform checks to see if application has sufficient rights to save data to hard drive.

Creating a store

Before saving data to isolated storage must scope it. For most apps choose one of:

  • Assembly / Machine - store to keep info specific to calling assembly and local machine. Used for creating application-level data. Create as follows:
IsolatedStorageFile machineStorage = IsolatedStorageFile.GetMachineStoreForAssembly();

The store is specific to the assembly calling it whether main exe in windows Forms project or DLL that is part of a larger project.

  • Assembly / User - store to keep info specific to calling assembly and current user. Used for user-level data. Create as follows:
IsolatedStorageFile userStorage = IsolatedStorageFile.GetUserStoreForAssembly();

For Click-Once deployed applications isolated storage also supports application-level store that supports both machine-level and user-level stores. Application-level stores work only within Click-Once applications as they have their own evidence that may or may not be valid for local applications.

IsolatedStorageFileStream

Derived from FileStream.

Encapsulates stream to read, write and create files in isolated storage.

Creation example:

IsolatedStorageFile userStorage = IsolatedStorageFile.GetUserStoreForAssembly();

IsolatedStorageFileStream userStream = new IsolatedStorageFileStream("UserSettings.set", FileMode.Create, userStore);

Reading example:

IsolatedStorageFileStream userStream = new IsolatedStorageFileStream("UserSettings.set", FileMode.Open, userStore);

No support to check if file directly exists (like File.Exists). Instead ask store for set of files matching mask, e.g.

string[] files = userStore.GetFileNames("UserSettings.set");

if (files.Length ==0)
{
    Console.WriteLine("No data saved for the user");
}

Directories in Isolated Storage

IsolatedStorageFile supports CreateDirectory method., e.g.

userStore.CreateDirectory("SomeDir");

IsolatedStorageFileStream userStream = new IsolatedStorageFileStream(@"SomeDir\UserSettings.set", FileMode.Open, userStore);

Like files, to test for existence ask store for set of files matching mask, e.g.

string[] files = userStore.GetDirectoryNames("SomeDir");

if (files.Length ==0)
{
    userStore.CreateDirectory("SomeDir");  
}

Permitting Isolated Storage

Before accessing isolated storage, permission must be granted, e.g.

[IsolatedStorageFilePermission(SecurityAction.Demand)]
class program
{

}

Attribute ensures any calls to isolated storage within class will succeed. If code does not have permissions, it will help system administrators to understand what security permissions the application requires.

Attribute supports several properties that modify how isolated storage is used, e.g.

[IsolatedStorageFilePermission(SecurityAction.Demand, UserQuota=1024, UsgaeAllowed=IsolatedStorageContainment.AssemblyIsolationByUser)]
class program
{

}

UsageAllowed specifies type of usage

UsageQuota specifies overall size of storage per user.

Download