September 2, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

File I/O with Streams - Part 1 - An Excerpt from Inside C#, Second Edition

  • March 7, 2003
  • By Tom Archer
  • Send Email »
  • More Articles »

The FileStream Class

Let.s dive into some code. Consider this simple use of FileStream, which creates a file, writes out an array of bytes, and closes the file. The application then opens the file again, tests that the stream supports reading, and reads in the bytes one by one, converting each byte to a character and appending it to a string:
using System.IO;

public class StreamsIOApp
{
  public static void Main(string[] args)
  {
    byte[] buf1 = new Byte[]
      {76,101,116,32,116,104,101,114,101,
       32,98,101,32,108,105,103,104,116};

    Stream s = new FileStream("Foo.txt", FileMode.Create);
    s.Write(buf1, 0, buf1.Length);
    s.Close();

    s = new FileStream("Foo.txt", FileMode.Open);
    int i;
    string str = "";
    if (s.CanRead)
    {
      for (i = 0; (i = s.ReadByte()) != -1; i++)
      {
        str += (char)i;
      }
    }
    s.Close();
    Console.WriteLine(str);
  }
}

Here's the output:

Let there be light

Note that we.re using only the Stream (virtual) methods, so we can safely use a Stream reference to the derived FileStream object. Of course, we also could've written the code to use a FileStream reference:

//Stream s = new FileStream(
FileStream s = new FileStream(

It.s no coincidence that the streams methods tend to use bytes and byte arrays.you can visualize a stream as a byte array that might be attached to some memory buffer or to some disk file or device. Streams use the concept of an internal stream pointer: when you open the stream, the stream pointer is normally positioned at the first byte of the stream. Most streams support seeking.the ability to move the internal stream pointer to an arbitrary position. Therefore, instead of the create-write-close-open-read-close pattern, we can avoid closing and opening when we want to switch between writing and reading by seeking in between.

For example, the following continuation code reopens the same file, reports on its length and the position of the internal stream pointer, and then tests to see whether the stream supports seeking. If the stream does, we seek 13 bytes from one of the three relative starting points: SeekOrigin.Begin, SeekOrigin.Current, or SeekOrigin.End. If we write to the stream at that point, we.ll overwrite some of the data that was originally in the stream. We can then seek again.rather than closing and opening.before reading from the file:

byte[] buf2 = new Byte[]
  {97,112,112,108,101,115,97,117,99,101};
s = new FileStream("Foo.txt", FileMode.Open);
Console.WriteLine("Length: {0}, Position: {1}", s.Length, s.Position);
if (s.CanSeek)
{
  s.Seek(13, SeekOrigin.Begin);
  Console.WriteLine("Position: {0}", s.Position);
  s.Write(buf2, 0, buf2.Length);
}

str = "";
s.Seek(0, SeekOrigin.Begin);
for (i = 0; (i = s.ReadByte()) != -1; i++)
{
  str += (char)i;
}
Console.WriteLine(str);

Here's the output:

Length: 18, Position: 0
Position: 13
Let there be applesauce

Note that if you want to Seek from SeekOrigin.End, you should supply a negative value. However, if you want to Seek from SeekOrigin.Current, you can supply either a positive or negative value depending on which direction you want to go. In addition to using the Length and Position properties, you could even arbitrarily set the length by using Stream.SetLength.

str = "";
s.SetLength(s.Length - 4);
s.Seek(0, SeekOrigin.Begin);
for (i = 0; (i = s.ReadByte()) != -1; i++)
{
  str += (char)i;
}
s.Close();
Console.WriteLine(str);

This is the output:

Let there be apples

A stream must support both writing and seeking for SetLength to work.

Instead of using Seek, we could achieve the same effect by using the Position property:

//s.Seek(0, SeekOrigin.Begin);
s.Position = 0;

Finally, we could append to the end of the file. If you open a stream for appending, the internal stream pointer will be positioned at the end of the stream, not the beginning, as is normally the case. FileMode.Append can be used only in conjunction with FileAccess.Write.any attempt to read from the file will fail and throw an ArgumentException. Hence, once we.ve opened the file for appending and have written out some more data, we must close it and reopen it before reading from it:

byte[] buf4 = new Byte[]
  {32,97,110,100,32,112,101,97,114,115};
s = new FileStream("Foo.txt", FileMode.Append, FileAccess.Write);
s.Write(buf4, 0, buf4.Length);
s.Close();

s = new FileStream("Foo.txt", FileMode.Open);
str = "";
for (i = 0; (i = s.ReadByte()) != -1; i++)
{
  str += (char)i;
}
Console.WriteLine(str);
s.Close();

This is the output:

Let there be apples and pears

If you construct a FileStream object with FileMode.Append and don.t specify the access permissions, the object will be constructed with read permission. Therefore, the following statements are equivalent:

s = new FileStream(
//  "Foo.txt", FileMode.Append, FileAccess.Write);
    "Foo.txt", FileMode.Append);

As a general rule, you should always protect your file-handling operations with the appropriate exception-handling code. In this article, we'll keep this code to a minimum. However, the subject of exception handling is covered in much more detail in Chapter 12, "Error Handling with Exceptions" of my Inside C# book.



Page 2 of 6



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel