Chapter 36. Streams

At some very primitive level, all pieces of data are just piles of bytes. The computer doesn't really store invoices, employee records, and recipes. At its most basic level, the computer stores bytes of data (or even bits, but the computer naturally groups them in bytes). It is only when a program interprets those bytes that they acquire a higher-level meaning that is valuable to the user.

Although you generally don't want to treat high-level data as undifferentiated bytes, there are times when thinking of the data as bytes lets you handle it in more uniform ways.

One type of byte-like data is the stream, an ordered series of bytes. Files, data flowing across a network, messages moving through a queue, and even the memory in an array all fit this description.

Defining the abstract idea of a stream lets applications handle these different types of objects uniformly. If an encryption or serialization routine manipulates a generic stream of bytes, it doesn't need to know whether the stream represents a file, a chunk of memory, plain text, encrypted text, or data flowing across a network.

Visual Studio provides several classes for manipulating different kinds of streams. It also provides higher-level classes for working with this kind of data at a more abstract level. For example, it provides classes for working with streams that happen to represent files and directories.

This chapter describes some of the classes you can use to manipulate streams. It explains lower-level classes that you may use only rarely and higher-level classes that let you read and write strings and files relatively easily.

The following table summarizes the most useful stream classes.

CLASS

USE

FileStream

Read and write bytes in a file.

MemoryStream

Read and write bytes in memory.

BinaryReader, BinaryWriter

Read and write specific data types in a stream.

StringReader, StringWriter

Read and write text with or without new lines in a string.

StreamReader, StreamWriter

Read and write text with or without new lines in a stream (usually a file stream).

STREAM

The Stream class defines properties and methods that derived stream classes must provide. These let the program perform relatively generic tasks with streams such as determining whether the stream allows writing.

The following table describes the Stream class's most useful properties.

PROPERTY

PURPOSE

CanRead

Returns True if the stream supports reading.

CanSeek

Returns True if the stream supports seeking to a particular position in the stream.

CanTimeout

Returns True if the stream supports timing out of read and write operations.

CanWrite

Returns True if the stream supports writing.

Length

Returns the number of bytes in the stream.

Position

Returns the stream's current position in its bytes. For a stream that supports seeking, the program can set this value to move to a particular position.

ReadTimeout

Determines the number of milliseconds that a read operation will wait before timing out.

WriteTimeout

Determines the number of milliseconds that a write operation will wait before timing out.

The following table describes the Stream class's most useful methods.

METHOD

PURPOSE

BeginRead

Begins an asynchronous read.

BeginWrite

Begins an asynchronous write.

Close

Closes the stream and releases any resources it uses (such as file handles).

EndRead

Waits for an asynchronous read to finish.

EndWrite

Ends an asynchronous write.

Flush

Flushes data from the stream's buffers into the underlying storage medium (device, file, memory, and so forth).

Read

Reads bytes from the stream and advances its position by that number of bytes.

ReadByte

Reads a byte from the stream and advances its position by one byte.

Seek

If the stream supports seeking, sets the stream's position.

SetLength

Sets the stream's length. If the stream is currently longer than the new length, it is truncated. If the stream is shorter than the new length, it is enlarged. The stream must support both writing and seeking for this method to work.

Write

Writes bytes into the stream and advances the current position by this number of bytes.

WriteByte

Writes one byte into the stream and advances the current position by one byte.

You can learn about the other members of the Stream class at msdn.microsoft.com/system.io.stream.aspx.

FILESTREAM

The FileStream class provides a stream representation of a file.

The FileStream class's parent class Stream defines most of its properties and methods. See the preceding section "Stream" for descriptions of those properties and methods.

FileStream adds two useful new properties to those it inherits from Stream. First, IsAsync returns True if the FileStream was opened asynchronously. Second, the Name property returns the file name passed into the object's constructor.

The class also adds two new useful methods to those it inherits from Stream. The Lock method locks the file, so other processes can read it but not modify it. Unlock removes a previous lock.

Overloaded versions of the FileStream class's constructor let you specify the following:

  • A file name or handle, the file mode (Append, Create, CreateNew, Open, OpenOrCreate, or Truncate)

  • Access mode (Read, Write, or ReadWrite)

  • File sharing (Inheritable, which allows child processes to inherit the file handle, None, Read, Write, or ReadWrite)

  • A buffer size

  • File options (Asynchronous, DeleteOnClose, Encrypted, None, RandomAccess, SequentialScane, or WriteThrough)

Example program FileStreamWrite uses the following code to create a file. It creates a file and uses a Universal Transformation Format (UTF) UTF8Encoding object to convert a string into an array of bytes. It writes the bytes into the file and then closes the FileStream.

Dim file_name As String = Application.StartupPath & "	est.txt"
Using file_stream As New FileStream(file_name, FileMode.Create)
    Dim bytes As Byte() = New UTF8Encoding().GetBytes("Hello world!")

    file_stream.Write(bytes, 0, bytes.Length)
    file_stream.Close()
End Using
                                                  
FILESTREAM

As this example demonstrates, the FileStream class provides only low-level methods for reading and writing files. These methods let you read and write bytes, but not integers, strings, or the other types of data that you are more likely to want to use.

The BinaryReader and BinaryWriter classes let you read and write binary data more easily than the FileStream class does. The StringReader and StringWriter classes let you read and write string data more easily than the other classes. See the section "StringReader and StringWriter" describing these classes later in this chapter for more information.

MEMORYSTREAM

Like FileStream, the MemoryStream class inherits from the Stream class. This class represents a stream with data stored in memory. Like the FileStream, it provides only relatively simple methods for reading and writing data. Usually, you will want to attach a higher-level object to the MemoryStream to make using it easier.

Example program MemoryStreamWrite uses the following code to write and read from a MemoryStream object. It first creates the MemoryStream. It then creates a BinaryWriter attached to the MemoryStream and uses it to write some text into the stream. Next, the program creates a BinaryReader object attached to the same MemoryStream. It uses the stream's Seek method to rewind the stream to its beginning, and then uses the BinaryReader object's ReadString method to read the string out of the MemoryStream.

Dim memory_stream As New MemoryStream()
Dim binary_writer As New BinaryWriter(memory_stream)
binary_writer.Write("Peter Piper picked a peck of pickled peppers.")

Dim binary_reader As New BinaryReader(memory_stream)
memory_stream.Seek(0, SeekOrigin.Begin)
MessageBox.Show(binary_reader.ReadString())
binary_reader.Close()
                                                  
MEMORYSTREAM

The following example does the same things as the previous example, except that it uses the StreamWriter and StreamReader classes instead of BinaryWriter and BinaryReader. Note that this version must call the StreamWriter class's Flush method to ensure that all of the text is written into the MemoryStream before it can read the memory using the StreamReader.

Using memory_stream As New MemoryStream()
    Dim stream_writer As New StreamWriter(memory_stream)
    stream_writer.Write("Peter Piper picked a peck of pickled peppers.")
    stream_writer.Flush()

    Dim stream_reader As New StreamReader(memory_stream)
    memory_stream.Seek(0, SeekOrigin.Begin)
    MessageBox.Show(stream_reader.ReadToEnd())
    stream_reader.Close()
End Using

BUFFEREDSTREAM

The BufferedStream class adds buffering to another stream class. For example, you can create a BufferedStream class attached to a network stream that communicates with another application through sockets. The BufferedStream class buffers data passing through the network connection.

Most programs don't need to explicitly create their own buffered streams, so this class isn't described further here. See the online help for more information.

BINARYREADER AND BINARYWRITER

The BinaryReader and BinaryWriter classes are not stream classes. Instead, they are helper classes that work with stream classes. They let you read and write data in files using a specific encoding. For example, the BinaryReader object's ReadInt32 method reads a 4-byte (32-bit) signed integer from the stream. Similarly, the ReadUInt16 method reads a 2-byte (16-bit) unsigned integer.

These classes still work at a relatively low level, and you should generally use higher-level classes to read and write data. For example, you shouldn't tie yourself to a particular representation of an integer unless you really must.

BinaryReader and BinaryWriter objects are attached to stream objects that provide access to the underlying bytes. Both of these classes have a BaseStream property that returns a reference to the underlying stream. Note also that the Close method provided by each of these classes automatically closes the underlying stream.

The following table describes the BinaryReader class's most useful methods.

METHOD

PURPOSE

Close

Closes the BinaryReader and its underlying stream.

PeekChar

Reads the stream's next character but does not advance the reader's position, so other methods can still read the character later.

Read

Reads characters from the stream and advances the reader's position.

ReadBoolean

Reads a Boolean from the stream and advances the reader's position by 1 byte.

ReadByte

Reads a byte from the stream and advances the reader's position by 1 byte.

ReadBytes

Reads a number of bytes from the stream into a byte array and advances the reader's position by that number of bytes.

ReadChar

Reads a character from the stream, and advances the reader's position according to the stream's encoding and the character.

ReadChars

Reads a number of characters from the stream, returns the results in a character array, and advances the reader's position according to the stream's encoding and the characters.

ReadDecimal

Reads a decimal value from the stream and advances the reader's position by 16 bytes.

ReadDouble

Reads an 8-byte floating-point value from the stream and advances the reader's position by 8 bytes.

ReadInt16

Reads a 2-byte signed integer from the stream and advances the reader's position by 2 bytes.

ReadInt32

Reads a 4-byte signed integer from the stream and advances the reader's position by 4 bytes.

ReadInt64

Reads an 8-byte signed integer from the stream and advances the reader's position by 8 bytes.

ReadSByte

Reads a signed byte from the stream and advances the reader's position by 1 byte.

ReadSingle

Reads a 4-byte floating-point value from the stream and advances the reader's position by 4 bytes.

ReadString

Reads a string from the current stream and advances the reader's position past it. The string begins with its length.

ReadUInt16

Reads a 2-byte unsigned integer from the stream and advances the reader's position by 2 bytes.

ReadUInt32

Reads a 4-byte unsigned integer from the stream and advances the reader's position by 4 bytes.

ReadUInt64

Reads an 8-byte unsigned integer from the stream and advances the reader's position by 8 bytes.

The following table describes the BinaryWriter class's most useful methods.

METHOD

PURPOSE

Close

Closes the BinaryWriter and its underlying stream.

Flush

Writes any buffered data into the underlying stream.

Seek

Sets the position within the stream.

Write

Writes a value into the stream. This method has many overloaded versions that write characters, arrays of characters, integers, strings, unsigned 64-bit integers, and so forth.

You can learn about the other members of the BinaryWriter and BinaryReader classes at msdn.microsoft.com/system.io.binarywriter.aspx and msdn.microsoft.com/system.io.binaryreader.aspx, respectively.

TEXTREADER AND TEXTWRITER

The TextReader and TextWriter classes are also not stream classes, but they provide properties and methods for working with text, which is stream-related. In particular, the StreamWriter and StreamReader classes derived from TextReader and TextWriter are associated with streams.

TextReader and TextWriter are abstract (MustInherit) classes that define behaviors for derived classes that read or write text characters. For example, the StringWriter and StreamWriter classes derived from TextWriter let a program write characters into a string or stream, respectively. Normally, you would use these derived classes to read and write text, but you might want to use the TextReader or TextWriter classes to manipulate the underlying classes more generically. You may also encounter a method that requires a TextReader or TextWriter object as a parameter. In that case, you could pass the method either a StringReader/StringWriter or a StreamReader/StreamWriter. For more information on these, see the sections "StringReader and StringWriter" and "StreamReader and StreamWriter" later in this chapter.

The following table describes the TextReader object's most useful methods.

METHOD

PURPOSE

Close

Closes the reader and releases any resources that it is using.

Peek

Reads the next character from the text without changing the reader's state, so other methods can read the character later.

Read

Reads data from the input. Overloaded versions of this method read a single character or an array of characters up to a specified length.

ReadBlock

Reads data from the input into an array of characters.

ReadLine

Reads a line of characters from the input and returns the data in a string.

ReadToEnd

Reads any remaining characters in the input and returns them in a string.

The TextWriter class has three useful properties. Encoding specifies the text's encoding (ASCII, UTF-8, Unicode, and so forth).

FormatProvider returns an object that controls formatting. For example, you can build a FormatProvider object that knows how to display numbers in different bases (such as hexadecimal or octal).

The NewLine property gets or sets the string used by the writer to end lines. Usually, this value is something similar to a carriage return or a carriage return plus a line feed.

The following table describes the TextWriter object's most useful methods.

METHOD

PURPOSE

Close

Closes the writer and releases any resources it uses.

Flush

Writes any buffered data into the underlying output.

Write

Writes a value into the output. This method has many overloaded versions that write characters, arrays of characters, integers, strings, unsigned 64-bit integers, and so forth.

WriteLine

Writes data into the output followed by the new line sequence.

You can learn about the other members of the TextWriter and TextReader classes at msdn.microsoft.com/system.io.textwriter.aspx and msdn.microsoft.com/system.io.textreader.aspx, respectively.

STRINGREADER AND STRINGWRITER

The StringReader and StringWriter classes let a program read and write text in a string.

These classes are derived from TextReader and TextWriter and inherit the definitions of most of their properties and methods from those classes. See the preceding section "TextReader and TextWriter" for details.

The StringReader provides methods for reading lines, characters, or blocks of characters from the string. Its ReadToEnd method returns any of the string that has not already been read. The StringReader class's constructor takes as a parameter the string that it should process.

The StringWriter class lets an application build a string. It provides methods to write text into the string with or without a new-line character. Its ToString method returns the StringWriter class's string.

The StringWriter stores its string in an underlying StringBuilder class. StringBuilder is designed to make incrementally building a string more efficient. For example, if an application needs to build a very large string by concatenating a series of long substrings, it may be more efficient to use a StringBuilder rather than adding the strings to a normal String variable. StringWriter provides a simple interface to the StringBuilder class.

The most useful method provided by StringWriter that is not defined by the TextWriter parent class is GetStringBuilder. This method returns a reference to the underlying StringBuilder object that holds the class's data.

Example program StringWriterReader uses the following code to demonstrate the StringWriter and StringReader classes. It creates a StringWriter object and uses its WriteLine method to add two lines to the string. It then displays the result of the writer's ToString method. This method returns the writer's current contents. Next, the program creates a StringReader, passing its constructor the string from which it will read. It closes the StringWriter because it is no longer needed. The code displays the result of the StringReader class's ReadLine method. Because the StringWriter created the string as two separate lines, this displays only the first line, "The quick brown fox." Next, the code uses the StringReader class's ReadToEnd method to read and display the rest of the text, "jumps over the lazy dog." The code finishes by closing the StringReader.

' Use a StringWriter to write into a string.
Using string_writer As New StringWriter()
    string_writer.WriteLine("The quick brown fox")
    string_writer.WriteLine("jumps over the lazy dog.")
    MessageBox.Show(string_writer.ToString)

    Use a StringReader to read from the string.
    Using string_reader As New StringReader(string_writer.ToString)
        string_writer.Close()
        MessageBox.Show(string_reader.ReadLine())
        MessageBox.Show(string_reader.ReadToEnd())
        string_reader.Close()
    End Using
End Using
                                                  
STRINGREADER AND STRINGWRITER

STREAMREADER AND STREAMWRITER

The StreamReader and StreamWriter classes let a program read and write data in a stream. The underlying stream is usually a FileStream. You can pass a FileStream into these classes' constructors, or you can pass a file name and the object will create a FileStream automatically.

The StreamReader provides methods for reading lines, characters, or blocks of characters from the stream. Its ReadToEnd method returns any of the stream that has not already been read. The EndOfStream method returns True when the StreamReader has reached the end of the stream.

Example program ReadLines uses the following code fragment to read the lines from a file and add them to the a ListBox control:

Do Until stream_reader.EndOfStream()
    lstValues.Items.Add(stream_reader.ReadLine())
Loop
                                                  
STREAMREADER AND STREAMWRITER

The StreamWriter class provides methods to write text into the stream with or without a new-line character.

StreamReader and StreamWriter are derived from the TextReader and TextWriter classes and inherit the definitions of most of their properties and methods from those classes. See the section "TextReader and TextWriter" earlier in this chapter for a description of these properties and methods.

The StreamWriter class adds a new AutoFlush property that determines whether the writer flushes its buffer after every write.

Example program StreamWriterReader uses the following code to demonstrate the StreamReader and StreamWriter classes. It generates a file name and passes it into a StreamWriter class's constructor. It uses the StreamWriter class's Write and WriteLine methods to place two lines of text in the file. It then closes the file. If you were to open the file now with a text editor, you would see the text. The program then creates a new StreamReader, passing its constructor the same file name. It uses the reader's ReadToEnd method to grab the file's contents and displays the results.

Dim file_name As String = Application.StartupPath & "	est.txt"
Using stream_writer As New StreamWriter(file_name)
    stream_writer.Write("The quick brown fox")
    stream_writer.WriteLine(" jumps over the lazy dog.")
    stream_writer.Close()
End Using

Using stream_reader As New StreamReader(file_name)
    MessageBox.Show(stream_reader.ReadToEnd())
    stream_reader.Close()
End Using
                                                  
STREAMREADER AND STREAMWRITER

This example would have been much more awkward using a FileStream object's lower-level Write and Read methods to manipulate byte arrays. Compare this code to the example in the "FileStream" section earlier in this chapter.

OPENTEXT, CREATETEXT, AND APPENDTEXT

The File class in the System.IO namespace provides four shared methods that are particularly useful for working with StreamReader and StreamWriter objects associated with text files. The following table summarizes these four methods.

METHOD

PURPOSE

Exists

Returns True if a file with a given path exists.

OpenText

Returns a StreamReader that lets you read from an existing text file.

CreateText

Creates a new text file, overwriting any existing file at the given path, and returns a StreamWriter that lets you write into the new file.

AppendText

If the indicated file does not exist, creates the file. Whether the file is new or previously existing, returns a StreamWriter that lets you append text at the end of the file.

The following code demonstrates the Exists and OpenText methods. First, it uses Exists to see if the file exists. If the file does exist, the code uses OpenText to open the file and get a StreamReader associated with it. It uses the StreamReader class's ReadToEnd method to display the file's text in the text box txtData.

Dim file_name As String = Application.StartupPath & "	est.txt"
If Not Exists(file_name) Then
     txtData.Text = " < File not found > "
Else
     Using sr As StreamReader = OpenText(file_name)
         txtData.Text = sr.ReadToEnd()
         sr.Close()
     End Using
End If
                                                  
OPENTEXT, CREATETEXT, AND APPENDTEXT

The following code demonstrates the CreateText method. The code uses CreateText to create a new text file named test.txt. If test.txt already exists, CreateText overwrites it without warning. The program uses the StreamWriter returned by CreateText to write the contents of the txtData text box into the file and closes the file.

Dim file_name As String = Application.StartupPath & "	est.txt"
Using sw As StreamWriter = CreateText(file_name)
    sw.Write(txtData.Text)
    sw.Close()
End Using
                                                  
OPENTEXT, CREATETEXT, AND APPENDTEXT

The following code demonstrates the AppendText method. The AppendText method creates the file if it doesn't already exist, or opens it for appending if it does exist. The program uses the StreamWriter returned by AppendText to write into the file and then closes the file.

Dim file_name As String = Application.StartupPath & "	est.txt"
Using sw As StreamWriter = AppendText(file_name)
    sw.Write(txtData.Text)
    sw.Close()
End Using
                                                  
OPENTEXT, CREATETEXT, AND APPENDTEXT

Example program OpenCreateAppendText uses these code snippets to see if a file exists, create a new file, open an existing file, append to an existing file, and read from a file.

CUSTOM STREAM CLASSES

Visual Studio provides a few other stream classes with more specialized uses.

The CryptoStream class applies a cryptographic transformation to data that passes through it. For example, if you attach a CryptoStream to a file using a particular cryptographic transformation and then use it to write data, the CryptoStream automatically transforms the data and produces an encrypted file. Similarly, you can use a CryptoStream to read an encrypted file and recover the original text.

The NetworkStream class represents a socket-based stream over a network connection. You can use this class to make different applications communicate over a network.

Three other special uses of streams are standard input, standard output, and standard error. Console applications define these streams for reading and writing information to and from the console. An application can interact directly with these streams by accessing the Console.In, Console.Out, and Console.Error properties. It can change these streams to new stream objects such as StreamReaders and StreamWriters by calling the Console.SetIn, Console.SetOut, and Console.SetError methods.

SUMMARY

Streams let a program consider a wide variety of data sources in a uniform way. If a subroutine takes a stream as a parameter, it doesn't need to worry about whether the stream is attached to a string, file, block of memory, or network connection.

Many applications use the StringReader and StringWriter classes to read and write text in strings, and the StreamReader and StreamWriter classes to read and write text in streams (usually files). The Exists, OpenText, CreateText, and AppendText methods are particularly useful for working with StreamReader and StreamWriter objects associated with text files.

The other stream classes are often used at lower levels or as more abstract classes to allow a routine to process different kinds of streams in a uniform way. If you focus on these four classes (StringReader, StringWriter, StreamReader, and StreamWriter), you will quickly learn how to perform the most common stream operations.

Programs often use the StreamReader and StreamWriter classes to read and write files. Chapter 38, "File-System Objects," describes classes that let a Visual Basic application interact with the file system in other ways. These classes let a program examine, rename, move, and delete files and directories.

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

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