User Tools

Site Tools


notes:csharp:io

I/O and Streams in .NET

FileStream

Instantiate a FileStream using the FileStream's ctor:

// Create a read-only FileStream.
FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
 
// Create a read/write FileStream.
FileStream stream = new FileStream(path, FileMode.Open);
 
// Create or open a write-only FileStream.
FileStream stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
 
// Create a write-only FileStream.
FileStream stream = new FileStream(path, FileMode.Append);
 
// Create a write-only FileStream.
FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Write);

Instantiate a FileStream using static methods of the File class:

// Try to open a file for reading. Throw FileNotFoundException if the file does not exist.
FileStream stream = File.OpenRead(path);
 
// Create a read/write FileStream. If the file already exists, truncate its contents.
FileStream stream = File.Create(path);
 
// Create a write-only stream and position it at the beginning of a file.
// File.OpenWrite may leave old content in the file.
FileStream stream = File.OpenWrite(path);

Example: Read and write an entire file in one step using static methods of the File class:

The File.AppendAllText method is particularly useful in logging to a file.

// Read an entire file in one step.
string str = File.ReadAllText(path);
 
string[] lines = File.ReadAllLines(path);
 
byte[] bytes = File.ReadAllBytes(path);
 
// Write an entire file in one step.
File.WriteAllText(path, "test");
 
File.WriteAllLines(path, new string[] { "one", "two", "three" });
 
File.WriteAllBytes(path, new byte[] { 30, 128, 67 });
 
File.AppendAllText(path, "appended text");

Example: Read strings from a file using File.ReadLines. Each string represent an integer. The File.ReadLines method is more efficient than File.ReadAllLines because it returns an IEnumerable<T> collection rather than reading an entire file in one step:

IEnumerable<string> list = File.ReadLines(path);
 
var nums = File.ReadLines(path2)
    .Select(line =>
    {
        int num;
        if (Int32.TryParse(line, out num))
            return num;
        else
            throw new ArgumentException("NaN");
    });
int cnt = nums.Count(); // the number of integers read

Stream

The Stream class is an abstract class providing basic infrastructure for stream functionality.

The Stream class works only with bytes. Use adapters such as StreamReader/StreamWriter or BinaryReader/BinaryWriter to read or write strings, integers, or XML elements.

The Stream.Read method may leave a chunk of bytes unread in a stream. You can be certain you've reached the end of the stream only when it returns 0. The BinaryReader provides better facilities for reading multiple bytes from a stream.

Example: Read and write bytes to/from a FileStream:

using (Stream stream = new FileStream(path, FileMode.OpenOrCreate))
{
    // Write a single byte.
    stream.WriteByte(10);
 
    // Append a sequence of bytes.
    byte[] buffer = { 20, 30, 40 };
    stream.Write(buffer, 0, buffer.Length);
 
    Console.WriteLine(stream.Position); // 4
    // The stream is: 10, 20, 30, 40
 
    // Change the zero-based position in the stream.
    stream.Position = 1; // points to 20
 
    // Read two sequential bytes.
    Console.WriteLine(stream.ReadByte()); // 20
    Console.WriteLine(stream.ReadByte()); // 30
 
    // Read the first byte.
    stream.Position = 0; // points to 10
    Console.WriteLine(stream.ReadByte()); // 10
 
    // Read bytes from the beginning of the stream to the buffer.
    // Return the number of bytes read (less than or equal to the count arg).
    stream.Position = 0;
    Console.WriteLine(stream.Read(buffer, 0, buffer.Length)); // 3
    // The buffer is: 10, 20, 30
}

Example: Convert a string into an array of bytes and write it to a file. Then, read bytes one-by-one from a file and convert them to a string:

// Convert a string into an array of bytes and write it to a file.
using (Stream stream = new FileStream(path, FileMode.OpenOrCreate))
{
    byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello!");
    stream.Write(data, 0, data.Length);
}
 
// Read bytes one-by-one from a file and convert then into a string.
using (Stream stream = new FileStream(path, FileMode.OpenOrCreate))
{
    byte[] data = new byte[stream.Length];
 
    for (int i = 0; i < stream.Length; i++)
        data[i] = (byte)stream.ReadByte();
 
    Console.WriteLine(System.Text.Encoding.UTF8.GetString(data));
}

MemoryStream

MemoryStream may be useful when you need random access to a non-seekable stream.

Example: Copy a stream (here a FileStream) into a MemoryStream:

FileStream stream = File.OpenRead(path);
MemoryStream ms = new MemoryStream();
 
try
{
    byte[] chunk = new byte[0x400]; // 1kB chunks
    int bytesCount = -1;
    while (bytesCount != 0)
    {
        bytesCount = stream.Read(chunk, 0, chunk.Length);
        if (bytesCount != 0)
            ms.Write(chunk, 0, bytesCount);
    }
}
finally
{
    // Always close FileStream or use a using block.
    stream.Close();
}
 
ms.Close(); // closing MemoryStream is optional

Example: Some operations on MemoryStream:

// Convert a string into MemoryStream.
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("Hello!"));
 
// Convert MemoryStream to a byte array.
byte[] bytes = ms.ToArray();
 
// Obtain a pointer to the underlying array storage.
// This array may be longer than the stream's length.
byte[] buffer = ms.GetBuffer();

Decorator streams

  • BufferedStream - decorates another stream with buffering capability
  • DeflateStream - compresses/decompresses streams
  • GZipStream - compresses/decompresses streams; uses a protocol to detect errors; conforms to standards
  • CryptoStream

Example: Read bytes from a file. A FileStream is wrapped in a 10kB BufferedStream. Closing the BufferedStream closes the underlying stream:

using (FileStream fs = File.OpenRead(path))
{
    // The underlying FileStream advances 10000 bytes after reading the first byte.
    using (BufferedStream bs = new BufferedStream(fs, 10000)) // 10kB buffer
    {
        bs.ReadByte();
        Console.WriteLine(fs.Position); // 10000
    }
}

Example: Compress and decompress a binary file using DeflateStream:

byte[] data = Enumerable.Repeat((byte)'x', 1024).ToArray();
 
using (Stream s = File.Create(path)) // FileStream
using (Stream ds = new DeflateStream(s, CompressionMode.Compress))
    for(int i=0; i<data.Length; ++i)
        ds.WriteByte(data[i]);
 
Console.WriteLine(new FileInfo(path).Length); // the length of the compressed file: 11
 
int b;
using (Stream s = File.OpenRead(path)) // FileStream
using (Stream ds = new DeflateStream(s, CompressionMode.Decompress))
    for (int i = 0; i < data.Length; ++i)
        b = ds.ReadByte();

Example: Compress and decompress a text file using GZipStream:

using (Stream s = File.Create(path))
using (Stream ds = new GZipStream(s, CompressionMode.Compress))
using (TextWriter w = new StreamWriter(ds))
    w.Write("a very long text goes here ...");
 
using (Stream s = File.OpenRead(path))
using (Stream ds = new GZipStream(s, CompressionMode.Decompress))
using (TextReader r = new StreamReader(ds))
    Console.Write(r.ReadToEnd());

Example: Compress a file in memory:

byte[] data = Enumerable.Repeat((byte)'x', 1024).ToArray();
 
MemoryStream ms = new MemoryStream();
using (Stream ds = new DeflateStream(ms, CompressionMode.Compress))
{
    ds.Write(data, 0, data.Length);
} // closes DeflateStream and the underlying MemoryStream
 
// We need to call ToArray because the MemoryStream is closed.
byte[] compressed = ms.ToArray();

Stream adapters

Text adapters:

  • TextReader, TextWriter - abstract base classes for StreamReader/StreamWriter and StringReader/StringWriter
  • StreamReader, StreamWriter - utilize a stream as a data store
  • StringReader, StringWriter - utilize StringBuilder as a data store

Binary adapters (for primitive types such as int, bool, string, and float)

  • BinaryReader, BinaryWriter - read and write primitive data types (e.g., bool, char, string, int, float) as well as arrays of the primitive data types

XML adapters

  • XmlReader, XmlWriter

Example: Write a line of text to a file using StreamWriter, and then read it using StreamReader. Use default encoding UTF-8:

using (FileStream fs = File.Create(path))
using (TextWriter writer = new StreamWriter(fs))
    writer.WriteLine("Hello!");
 
using (FileStream fs = File.OpenRead(path))
using (TextReader reader = new StreamReader(fs))
    Console.WriteLine(reader.ReadLine()); // read a single line
 
using (FileStream fs = File.OpenRead(path))
using (TextReader reader = new StreamReader(fs))
    Console.WriteLine(reader.ReadToEnd()); // read the entire file   

Example: Write a line of text to a file using StreamWriter with Unicode encoding:

using (FileStream fs = File.Create(path))
using (TextWriter writer = new StreamWriter(fs, System.Text.Encoding.Unicode))
    writer.WriteLine("Hello!");

Example: Append a line of text to the end of a file using StreamWriter:

using (TextWriter writer = new StreamWriter(path, true)) // true means append
    writer.WriteLine("Hello!");

Example: Manipulate a text file using shortcut methods of the File class:

using (TextWriter writer = File.CreateText(path)) // the content of the file is overridden
    writer.WriteLine("Hello!");
 
// Append a line of text to the end of a file using the static method File.AppendText.
using (TextWriter writer = File.AppendText(path)) // the new line is appended
    writer.WriteLine("There");
 
// Read text from a file one line at a time.
using (TextReader reader = File.OpenText(path))
    while (reader.Peek() > -1) // check if there is anything to read (EOF)
        Console.WriteLine(reader.ReadLine());
 
// Read text from a file one line at a time.
string line;
using (TextReader reader = File.OpenText(path))
    while ((line = reader.ReadLine()) != null) // check if there is anything to read (EOF)
        Console.WriteLine(line);
 
// Read an entire file to a string.
using (TextReader reader = File.OpenText(path))
    string str = reader.ReadToEnd();        

Example: StringReader is less useful than StreamReader. One of the uses may be obtaining an XmlReader from an XML string:

XmlReader reader = XmlReader.Create(new StringReader(xml));

Example: Save data of a Book instance using BinaryWriter and then read it using BinaryReader:

Book b = new Book { ID = 1, Title = "A Very Good Book" };
 
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite("book.bin")))
{
    writer.Write(b.ID);
    writer.Write(b.Title);
}
 
Book b2 = new Book();
using (BinaryReader reader = new BinaryReader(File.OpenRead("book.bin")))
{
    b2.ID = reader.ReadInt32();
    b2.Title = reader.ReadString();
}
...
public class Book
{
    public int ID { get; set; }
    public string Title { get; set; }
}

Example: Save a collection of a custom struct Point to a file using BinaryWriter:

List<Point> point = new List<Point>()
{
    new Point { X=1, Y=2, Z=3 },
    new Point { X=4, Y=5, Z=6 },
    new Point { X=7, Y=8, Z=9 }
};
 
using (FileStream stream = File.OpenWrite("points.dat"))
using (BinaryWriter writer = new BinaryWriter(stream))
{
    point.ForEach(p => { writer.Write(p.X); writer.Write(p.Y); writer.Write(p.Z); });
} 
...
public struct Point
{
    public int X;
    public int Y;
    public int Z;
}

Example: Read the entire contents of a stream into a byte array using BinaryReader:

using (FileStream stream = File.OpenRead(path))
{
    byte[] data = new BinaryReader(stream).ReadBytes((int)stream.Length);
    // ...
}

Example: Read integers from a file using BinaryReader:

List<int> nums = new List<int>();
 
using (BinaryReader reader = new BinaryReader(File.OpenRead(path)))
{
    long pos = 0;
    long size = reader.BaseStream.Length;
 
    while (pos < size)
    {
        nums.Add(reader.ReadInt32());
        pos += sizeof(Int32);
    }
}

Zip Files

You can use the ZipArchive and ZipFile classes to compress files in zip format. The classes are located in the System.IO.Compression namespace in the System.IO.Compression.dll assembly. This feature was introduced in .NET 4.5.

Example: Zip all the files in a specified folder:

ZipFile.CreateFromDirectory (@"C:\Documents", @"C:\Temp\docs.zip");

Example: Extract all the files from a zip file to a folder:

ZipFile.ExtractToDirectory (@"C:\Temp\docs.zip", @"C:\Documents");

File / FileInfo

Use static methods of the File class or instance methods of the FileInfo class.

When executing a single operation against the file system, it may be more efficient to use the static methods of the File class.

When executing multiple operations against a file, FileInfo may be a better choice.

Example: Use the static methods of the File class:

// Check if a file exists.
bool exists = File.Exists(path);
 
// Move a file. Throw an exception if the destination file already exists.
File.Move(sourcePath, destPath);
 
// Move a file. Do not throw an exception if the destination file already exists.
// destBackup is a backup of the replaced file.
File.Replace(sourcePath, destPath, destBackup);
 
// Copy a file.
File.Copy(sourcePath, destPath);
 
// Delete a file. Throw an UnauthorizedAccessException if the file is read-only.
File.Delete(path);
 
// Remove the read-only attribute on a file.
FileAttributes attrs = File.GetAttributes(path);
if ((attrs & FileAttributes.ReadOnly) > 0)
{
    attrs ^= FileAttributes.ReadOnly;
    File.SetAttributes(path, attrs);
}

Example: Use the instance methods of the FileInfo class:

FileInfo fileInfo = new FileInfo(sourcePath);
 
bool exists = fileInfo.Exists;
fileInfo.MoveTo(destPath);
fileInfo.Replace(destPath, destBackup);
fileInfo.CopyTo(destPath);
fileInfo.Delete();
new FileInfo(path).IsReadOnly = false;

Example: List file's permissions:

using System.Security.AccessControl;
...
FileSecurity sec = File.GetAccessControl(path);
AuthorizationRuleCollection rules = sec.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount));
foreach (FileSystemAccessRule rule in rules)
{
    Console.WriteLine(rule.AccessControlType); // Allow or Deny
    Console.WriteLine(rule.FileSystemRights); // e.g., FullControl, ReadAndExecute, Modify
    Console.WriteLine(rule.IdentityReference.Value); // e.g., BUILTIN\Administrators
}

Example: A helper method to copy a file with all intermediate directories if necessary:

public static long CopyFile(string sourceFileName, string destFileName)
{
    if (!File.Exists(sourceFileName))
        throw new FileNotFoundException();
 
    string destPathFull = Path.GetDirectoryName(destFileName);
    if (!Directory.Exists(destPathFull))
        Directory.CreateDirectory(destPathFull);
 
    long fileSize = (new FileInfo(sourceFileName)).Length;
    File.Copy(sourceFileName, destFileName, true);
 
    return fileSize;
}

Directory / DirectoryInfo

Use static methods of the Directory class or instance methods of the DirectoryInfo class.

When executing a single operation against the file system, it may be more efficient to use the static methods of the Directory class.

When executing multiple operations against a folder, DirectoryInfo may be a better choice.

Example: Use the static methods of the Directory class:

// Check if a folder exists.
bool exists = Directory.Exists(path);
 
// Create a folder. Throw UnauthorizedAccessException if you don't have sufficient permissions 
// to create a folder in the specific location.
DirectoryInfo dir = Directory.CreateDirectory(path);
 
// Move a folder.
Directory.Move(sourcePath, destPath);
 
// Delete a folder. Throw DirectoryNotFoundException when the folder does not exist.
Directory.Delete(path);
 
// Obtain a list of files in a given folder.
string[] files = Directory.GetFiles(path);
 
// Obtain a list of folders.
// Directory.EnumerateDirectories may be more efficient than DirectoryInfo.GetDirectories
IEnumerable<string> folders = Directory.EnumerateDirectories(path);

Example: Use the instance methods of the DirectoryInfo class:

DirectoryInfo dirInfo = new DirectoryInfo(path);
 
bool exists = dirInfo.Exists;
dirInfo.Create();
dirInfo.MoveTo(destPath);
dirInfo.Delete();
FileInfo[] files = dirInfo.GetFiles();
DirectoryInfo[] folders = dirInfo.GetDirectories();

Example: Set access control for a folder:

using System.Security.AccessControl;
...
DirectoryInfo dirInfo = new DirectoryInfo(path);
 
DirectorySecurity security = dirInfo.GetAccessControl();
security.AddAccessRule(
    new FileSystemAccessRule(
        "Everyone",
        FileSystemRights.ReadAndExecute,
        AccessControlType.Allow));
dirInfo.SetAccessControl(security);

Example: Obtain special folders:

// Obtain the name of the current directory.
string currDir = Directory.GetCurrentDirectory();
string currDir = Environment.CurrentDirectory;
 
// Set the current directory.
Directory.SetCurrentDirectory(path);
 
// Obtain the name of the common application data folder:
string dataDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
 
// Obtain the application base directory. It is usually the folder containing the program's executable.
string baseDir = AppDomain.CurrentDomain.BaseDirectory 

Path

Example: Combine a path and a file name:

string fullPath = Path.Combine(@"C:\Temp", "data.dbf");

Example: Show sections of a path.

string path = @"C:\Temp\MyFolder\MyFile.txt";
Console.WriteLine(Path.GetDirectoryName(path)); // C:\Temp\MyFolder
Console.WriteLine(Path.GetExtension(path));     // .txt
Console.WriteLine(Path.GetFileName(path));      // data.dbf
Console.WriteLine(Path.GetPathRoot(path));      // C:\

Example: Obtain random files and folders:

// Obtain a random file name.
string name = Path.GetRandomFileName();
 
// Create a unique, temporary file and return the full path of that file.
string tmpFile = Path.GetTempFileName();
 
// Obtain the path of the current user's temporary folder.
string tmpFolder = Path.GetTempPath();

DriveInfo

// The type of the file system (such as FAT or NTFS).
string driveFormat = DriveInfo.DriveFormat;
 
// The total free space on the hard drive. Do not take the user disk quota into account. 
long freeSpace = DriveInfo.TotalFreeSpace;
 
// The total free space on the hard drive. Take the user disk quota into account. 
long freeSpace = DriveInfo.AvailableFreeSpace;
 
// The type of drive such as a CD-ROM drive, fixed drive, or removable drive 
// i.e. a floppy disk drive or a USB flash drive.
// Corresponding DriveType enum values: DriveType.CDRom, DriveType.Fixed, DriveType.Removable.
DriveType driveType = DriveInfo.DriveType;
 
// The volume label string assigned to the drive (for example "C_Drive" or "Finance_Data").
string label = DriveInfo.VolumeLabel;
 
// The total size of the disk.
long totalSize = DriveInfo.TotalSize;
 
// Enumerate the current drives.
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo drive in drives)
{
    // ...
}

async

Example: Fill out a file with 5000 random bytes asynchronously:

using System.Threading.Tasks;
...
static void Main(string[] args)
{
    // Obtain exceptions if any.
    AggregateException exc = WriteToFileAsync().Exception;
}
 
public static async Task WriteToFileAsync()
{
    using (FileStream stream =
        new FileStream(@"C:\Temp\Test.dat",
            FileMode.Create, FileAccess.Write,
            FileShare.None, 4096,
            true)) // useAsync = true
    {
        byte[] data = new byte[5000];
        new Random().NextBytes(data);
 
        await stream.WriteAsync(data, 0, data.Length);
    }
}
notes/csharp/io.txt · Last modified: 2017/08/25 by leszek