Вы находитесь на страницах старой версии сайта.
Переходите на новую версию Interface.Ru

СТАТЬЯ
25.01.03

Предыдущая статья

Знакомство с Microsoft .NET Framework
     Часть 6. Потоки

© Алексей Федоров
Статья была опубликована в журнале "КомпьютерПресс" 5'2002

Мы продолжаем разговор о Microsoft .NET Framework и библиотеке классов .NET Framework Class Library. В этой статье мы рассмотрим пространство имен System.IO и классы, связанные с потоковым вводом-выводом.

Мы продолжаем разговор о Microsoft .NET Framework (см. КомпьютерПресс № 11-12’2001, 1-4’2002) и библиотеке классов .NET Framework Class Library. В этом номере мы рассмотрим пространство имен System.IO и классы, связанные с потоковым вводом-выводом.

В этой части нашего обзора библиотеки классов .NET Framework Class Library мы начнем знакомство с пространством имен System.IO и рассмотрим потоки, средства для их чтения и записи из них. В следующем номере мы расскажем также о входящих в пространство имен System.IO классах, предназначенных для работы с файловой системой.

Пространство имен System.IO

Это пространство имен содержит классы и другие типы данных, используемые для синхронной и асинхронной работы (чтение и запись) с потоками и файлами. Пространство имен System.IO включает классы, реализующие потоки, средства чтения и записи потоков, а также классы для работы с файловой системой. Основные классы, входящие в пространство имен System.IO, показаны на рис. 1.

Потоки

Класс Stream — это абстрактное представление последовательности байтов. Классы, наследующие от класса Stream, обеспечивают более специфические функции — работу с файлами (класс FileStream), памятью (класс MemoryStream) и сетью (класс NetworkStream). В целом потоки поддерживают операции чтения, записи, а также операции позиционирования. Отметим, что операция позиционирования требует определения текущей позиции и может не поддерживаться в потоках определенного типа, например в сетевых потоках. Для того чтобы проверить возможности того или иного типа потока, следует использовать свойства CanRead, CanWrite и CanSeek.

Свойство Length используется для нахождения длины потока — числа байт, содержащихся в потоке (значение типа Long). Для определения текущей позиции в потоке или для ее изменения используется свойство Position (значение типа Long).

Класс Stream содержит несколько методов, определяющих базовую функциональность всех потоков:

В следующей таблице показано, в каких случаях мы не можем использовать некоторые свойства и методы классов, унаследованных от класса Stream.

Как мы отмечали выше, класс Stream служит в качестве базового класса для нескольких типов потоков. На рис. 2 показаны классы, унаследованные от абстрактного класса Stream.

Отметим, что классы NetworkStream и CryptoStream реализованы в других пространствах имен — в пространстве имен System.Net.Sockets и System.Security. Cryptography соответственно.

Класс BufferedStream

Класс BufferedStream используется для буферизованных потоковых операций чтения и записи (но не той и другой одновременно) в другой поток. Этот класс создает буфер в памяти (по умолчанию размер буфера равен 4 Кбайт, но это значение может быть изменено с помощью перегруженного конструктора), который используется для кэширования данных. В результате повышается производительность операций чтения и записи — за счет сокращения числа обращений к функциям операционной системы.

С целью создания буфера для существующего потока используется конструктор класса BufferedStream, а в качестве входного параметра указывается существующий поток. Дальнейшие операции выполняются через методы класса BufferedStream, а все изменения в буфере сохраняются в потоке вызовом метода Flush.

Класс FileStream

Класс FileStream используется для чтения и записи файлов. Мы можем использовать этот класс для чтения и записи байтов, символов, строк и других типов данных. Класс FileStream поддерживает синхронное и асинхронное открытие файлов, синхронные операции чтения и записи (методы Read и Write), а также асинхронные операции чтения и записи (методы BeginRead и BeginWrite). Асинхронные операции завершаются вызовом методов EndRead и EndWrite соответственно. Режим по умолчанию — синхронный; для проверки режима мы используем свойство IsAsync. Для асинхронных операций необходим объект WaitHandle. Метод Seek используется для произвольного доступа к файлам. Свойство Position позволяет нам узнать или установить текущую позицию в потоке. Методы Lock и Unlock служат для предотвращения доступа ко всему файлу или к его части, а также для отмены ранее установленного запрета доступа. Свойство Length возвращает длину потока в байтах, а метод SetLength служит для задания длины потока. Методы ReadByte и WriteByte используются для чтения и записи одного байта. Для других примитивных типов нам необходимы классы BinaryReader и BinaryWriter соответственно.

От класса FileStream наследует класс IsolatedStorageFileSystem (пространство имен System.IO.IsolatedStorage), служащий для чтения, записи и создания файлов в изолированном хранилище. Изолированное хранилище предоставляет в наше распоряжение виртуальную файловую систему, позволяющую читать и записывать данные, недоступные извне. Изолированное хранилище обеспечивает изоляцию данных на уровне пользователя, сборки или домена приложения.

Класс MemoryStream

Класс MemoryStream может использоваться для создания потока, содержимое которого хранится не на диске и не в сети, а в памяти. Этот класс задействует байтовый массив, который может иметь либо фиксированную, либо произвольную длину. В последнем случае мы можем изменять размер массива, читать из него и записывать в него. Что касается потока с фиксированной длиной, то в него мы можем только записывать.

Для того чтобы выяснить число байтов, выделенных под поток в памяти, мы пользуемся свойством Capacity, возвращающим данные типа Integer. Свойство Length возвращает реальное число байтов в потоке (значение типа Long), а метод GetBuffer() возвращает массив байтов, в котором располагается поток. Для сохранения всего содержимого потока в байтовом массиве используется метод ToArray(). Метод WriteTo(Stream) служит для копирования всего потока в другой поток.

Следующий пример показывает, как использовать класс MemoryStream для создания нового потока в памяти, задания его содержимого и сохранения этого потока в файле:

‘————————————————————
‘ Использование класса MemoryStream
‘————————————————————
Imports System
Imports System.IO
Module VBDemo
Sub Main()
Dim Bytes() As Byte = New Byte(10) {}
Dim I As Integer
Dim MemStr As New MemoryStream()
Dim FileStr As New FileStream(“c:\temp\bytes.bin”, _
FileMode.CreateNew)
Dim Rand As System.Random = New System.Random()
For I = 0 To 9
Bytes(I) = Rand.Next(0, 100)
Next
MemStr.Write(Bytes, 0, I)
MemStr.WriteTo(FileStr)
MemStr.Close()
FileStr.Close()
End Sub
End Module

Ниже мы кратко рассмотрим еще два типа потоков, которые наследуют от класса Stream, но реализованы вне пространства имен System.IO.

Класс NetworkStream

Этот класс служит для посылки данных по сети. Класс NetworkStream реализует поток, не поддерживающий операцию позиционирования. Поэтому мы не можем использовать свойство Position и метод Seek при работе с потоком этого типа.

Более подробно сетевые функции в Microsoft .NET мы рассмотрим в одной из следующих частей данной статьи.

Класс CryptoStream

Этот класс реализует поток, который связывает потоки данных с криптографическими трансформациями. Класс CryptoStream реализован в пространстве имен System.Secutity.Cryptography.

Для шифрования и расшифровки потоков мы должны выбрать провайдеров, которые обеспечивают шифровку и расшифровку. В настоящее время мы можем выбирать между симметричными алгоритмами типа Encryption Standard (DES), RC2, Triple Data Encryption Standard (TripleDES) и Rijndael/AES, асимметричными алгоритмами (которые также известны как публичные ключи) типа RSA, DSA и хэш-алгоритмами типа MD5, SHA1, SHA256, SHA384 и SHA512. Пространство имен System.Security.Cryptography.X509 также содержит минимальную поддержку публичных сертификатов.

Классы, которые реализуют поддержку цифровой подписи в XML-документах, располагаются в пространстве имен System.Security.Cryptography.XML.

Большинство алгоритмов в пространстве имен System.Secutity.Cryptography реализовано на основе интерфейса Microsoft CryptoAPI. Некоторые алгоритмы шифрования — SHA256, SHA384, SHA512 и Rijndael/AES — в настоящее время не поддерживаются интерфейсом CryptoAPI.

После того как мы выберем провайдера, который обеспечивает нам шифровку и расшифровку: DESCryptoServiceProvider, RC2CryptoServiceProvider, TripleDESCryptoServiceProvider и т.п., мы будем использовать его с ключом и инициализационным вектором для зашифровки или расшифровки указанного потока.

Последний шаг — сохранение зашифрованного или расшифрованного потока в файле. В следующем примере показано, как зашифровать массив байтов, сохранить его в файле, а затем расшифровать на основе алгоритма DES. Мы используем здесь конструктор по умолчанию, который заполняет необходимые параметры выбранного нами алгоритма:

'----------------------------------------
' Использование класса CryptoStream
'----------------------------------------

Imports System
Imports System.IO
Imports System.Security.Cryptography

Module VBDemo

Sub Main()

Dim Bytes() As Byte = {65, 66, 67, 68, 69, 70, 71, 72, 73, 74}
Dim EncBytes() As Byte = New Byte(15) {}
Dim DecBytes() As Byte = New Byte(10) {}

Dim FileName As String = "c:\temp\text.enc"
Dim EncFile As New FileStream(FileName, FileMode.Create, _
FileAccess.Write)
Dim DES As New DESCryptoServiceProvider()
Dim DESEncrypt As ICryptoTransform = DES.CreateEncryptor()
Dim CryptoStreamEnc As New CryptoStream(EncFile, DESEncrypt, _
CryptoStreamMode.Write)

Console.WriteLine("Original Data")
ToHexArray(Bytes)

CryptoStreamEnc.Write(Bytes, 0, Bytes.Length)
CryptoStreamEnc.Close()
EncFile.Close()

EncFile = New FileStream(FileName, FileMode.Open, FileAccess.Read)
EncFile.Read(EncBytes, 0, EncFile.Length)
EncFile.Close()

Console.WriteLine("Encrypted Data")
ToHexArray(EncBytes)
Console.WriteLine()

Dim DecFile As New FileStream(FileName, FileMode.Open, _
FileAccess.Read)
Dim DESDecrypt As ICryptoTransform = DES.CreateDecryptor()
Dim CryptoStreamDec As New CryptoStream(DecFile, DESDecrypt, _
CryptoStreamMode.Read)
Dim Reader As New BinaryReader(CryptoStreamDec)

Console.WriteLine("Decrypted Data")
DecBytes = Reader.ReadBytes(10)
ToHexArray(DecBytes)

End Sub

Sub ToHexArray(ByVal A As Byte())

Dim I As Integer

For I = 0 To A.GetUpperBound(0)
Console.Write("0x{0:x2} ", A(I))
If I = 7 Then
Console.WriteLine()
End If
Next

End Sub

End Module

Использование фиксированной длины ключа поддерживается во всех алгоритмах шифрования, но для алгоритмов, реализованных на основе CryptoAPI, мы должны установить High Encryption Pack, входящий в состав Windows 2000 Service Pack, Windows NT 4.0 Service Pack 6a или Internet Explorer 5.5 для Windows Me, Windows 98 и Windows 95.

Чтение и запись потоков

Пространство имен System.IO содержит четыре пары классов для чтения и записи потоков: классы, используемые для чтения и записи последовательной серии байтов или символов. К этим классам относятся классы BinaryReader и BinaryWriter для работы с примитивными типами данных, определенными в Common Type System, а также классы для чтения и записи последовательностей символов — TextReader и TextWriter, строк — StringReader и StringWriter и потоков — StreamReader и StreamWriter.

Класс BinaryReader

Класс BinaryReader предоставляет нам механизмы для чтения примитивных типов как бинарных значений. Он содержит методы для чтения каждого типа, определенного в Common Type System — ReadBoolean, ReadByte (ReadBytes), ReadChar (ReadChars), ReadDecimal, ReadDouble и т.д.

Для получения потока, ассоциированного с BinaryReader, что неизменно происходит в конструкторе, мы можем использовать свойство BaseStream типа Stream. Метод PeekChar() возвращает следующий символ из потока, но не перемещает указатель текущей позиции — это означает, что мы можем использовать данный метод для проверки того, имеются ли в потоке еще символы, перед тем как прочитать их. Метод Read(Byte(), Integer, Integer) or Read(Char(), Integer, Integer) используется для чтения указанного числа символов или байтов из потока. Этот метод может применяться вместо методов для чтения примитивных типов данных.

Следующий пример показывает, как использовать класс BinaryReader для создания простой утилиты шестнадцатеричного дампа:

‘————————————————————
‘ Использование класса BinaryReader
‘————————————————————
Imports System
Imports System.IO
Module VBDemo
Sub Main()
Dim Bytes As Byte()
Dim I As Integer
Dim Reader As BinaryReader
Reader = New BinaryReader(File.OpenRead (“c:\demo.exe”))
While Reader.PeekChar() > -1
Bytes = Reader.ReadBytes(16)
For I = 0 To Bytes.GetUpperBound(0)
Console.Write(“0x{0:X2}|”, Bytes(I))
Next
Console.WriteLine()
End While
End Sub
End Module


Класс BinaryWriter

Мы используем класс BinaryWriter для записи данных примитивных типов в ассоциированный поток. Этот класс содержит метод Write(), у которого существуют перегруженные методы для записи значений различных типов в текущий поток.

Объединяя методы классов BinaryReader и BinaryWriter, мы можем написать код, который выполняет операции копирования типа той, что приведена в следующем примере:

‘————————————————————
‘ Использование класса BinaryWriter
‘————————————————————
Imports System
Imports System.IO
Module VBDemo
Sub Main()
Dim Bytes As Byte()
Dim Reader As BinaryReader
Dim Writer As BinaryWriter
Reader = New BinaryReader(File.OpenRead (“c:\demo.exe”))
Writer = New BinaryWriter(File.Create (“c:\demo_copy.exe”))
While Reader.PeekChar() > -1
Bytes = Reader.ReadBytes(1024)
Writer.Write(Bytes)
End While
Reader.Close()
Writer.Flush()
Writer.Close()
End Sub
End Module
Класс TextReader

Класс TextReader используется для чтения последовательности символов из ассоциированного потока. Этот класс служит основой для двух других классов: StreamReader и StringReader (рис. 3), которые мы рассмотрим ниже.

Класс TextReader содержит следующие методы, которые мы можем использовать в нашем коде:

Поскольку класс TextReader является абстрактным классом, мы не можем задействовать его напрямую — мы должны использовать либо класс StreamReader, либо класс StringReader (их мы рассмотрим ниже).

Класс StreamReader

Класс StreamReader применяется для чтения последовательности символов из указанного файла или другого потока. В следующих примерах показано, как использовать класс StreamReader, чтобы прочитать текстовый файл построчно и как одну строку:

‘————————————————————
‘ Использование класса StreamReader
‘————————————————————
Imports System
Imports System.IO
Module VBDemo
Sub Main()
Dim FileName As String = _
“C:\Program Files\Microsoft.NET\FrameworkSDK\ include\corsym.h”
Dim Reader As TextReader
Dim I As Int32
Reader = New StreamReader(FileName)
While Reader.Peek() > -1
Console.WriteLine(Reader.ReadLine)
I += 1
End While
Console.WriteLine(“Read {0:G} lines”, I)
Reader.Close()
End Sub
End Module

Если нас не интересует число строк, которые мы должны прочитать, и мы знаем, что объем файла не очень велик, мы можем использовать метод ReadToEnd, как это продемонстрировано в следующем примере:

‘————————————————————
‘ Использование класса StreamReader
‘————————————————————
Imports System
Imports System.IO
Module VBDemo
Sub Main()
Dim FileName As String = _
“C:\Program Files\Microsoft.NET\FrameworkSDK\ include\corsym.h”
Dim Reader As TextReader
Dim I As Int32
Reader = New StreamReader(FileName)
Console.WriteLine(Reader.ReadToEnd)
Reader.Close()
End Sub
End Module
Класс
StringReader

Класс StringReader служит для чтения символов из строк. В следующем примере показана возможность использования классов StreamReader и StringReader для чтения строки из текстового файла и чтения символов из этой строки как из потока:

‘————————————————————
‘ Использование класса StringReader
‘————————————————————
Imports System
Imports System.IO
Module VBDemo
Sub Main()
Dim FileName As String = _
“C:\Program Files\Microsoft.NET\FrameworkSDK\ include\corsym.h”
Dim Reader As TextReader
Dim S As String
Reader = New StreamReader(FileName)
S = Reader.ReadLine
Dim SReader As New StringReader(S)
While SReader.Peek > -1
Console.WriteLine(Chr(SReader.Read))
End While
Reader.Close()
End Sub
End Module
Класс
TextWriter

Класс TextWriter служит для записи последовательности символов в поток. Для байтовых операций записи мы должны использовать класс Stream, а для бинарной записи примитивных типов — класс BinaryWriter.

Класс TextWriter — это абстрактный класс, он служит в качестве базы для нескольких классов: классов StreamWriter, StringWriter, реализованных в пространстве имен System.IO, класса IntendedTextWriter из пространства имен System.CodeDom.Complier, а также классов HTTPWriter и HTMLWriter, реализованных в пространствах имен System.Web и System.Web.UI соответственно (рис. 4).

Класс StreamWriter

Класс StreamWriter применяется для вывода последовательности символов в той или иной кодировке. По умолчанию используется экземпляр класса UTF8Encoding для записи символов в кодировке Unicode UTF-8. Существует несколько перегруженных конструкторов, позволяющих указать поток, кодировку по умолчанию, задать кодировку, размер буфера и тип операции: требуется перезаписать существующий файл или информация должна быть к нему добавлена.

Свойство AutoFlush служит для указания, следует ли записывать содержимое буфера в поток после каждого вызова методов Write и WriteLine.

Класс StringWriter

Класс StringWriter используется для записи строки, которая хранится в классе StringBuilder, реализованном в пространстве имен System.Text. Более подробно об этом сказано в части 5 данной статьи (см. КомпьютерПресс № 4’2002).

Другие классы на базе класса TextWriter

Как мы отмечали выше, в библиотеке классов .NET Framework Class Library существует несколько дополнительных классов, базирующихся на абстрактном классе TextWriter:

На этом мы завершаем рассмотрение потоков и существующих в них средств чтения и записи. Тема следующей статьи данного цикла - классы из пространства имен System.IO, служащие для работы с файлами и каталогами.

Дополнительная информация

За дополнительной информацией обращайтесь в компанию Interface Ltd.

Обсудить на форуме Microsoft

Рекомендовать страницу

INTERFACE Ltd.
Телефон/Факс: +7 (495) 925-0049
Отправить E-Mail
http://www.interface.ru
Rambler's Top100
Ваши замечания и предложения отправляйте редактору
По техническим вопросам обращайтесь к вебмастеру
Дата публикации: 25.01.03