« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга


ПнВтСрЧтПтСбВс
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
    Популярное
Элементы управления .NET. Работа с TreeView

Как создать простой текст любой сложности: взаимодействие Microsoft Office 2007 + Mind Маnager

Функция CopyMetaFile

Активация служб терминалов в Windows Server 2003

Работа с HTTP протоколом

Понятие массивов

От автоpа

Использование PEAR для доступа к базе данных

Интерфейсы и Реализации

C++ Builder Работа с базой данных через ADO




    Архив файлов



    Сообщества

    Документация

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 152
Пользователей: 0

Статьи:: Программирование на Visual Basic 7 .NET) :: Глава 9. Ввод-вывод



отправить ссылку другу версия для печати  Обсудить на форуме

Глава 9. Ввод-вывод



Глава №9.

Ввод-вывод

В прежних версиях Visual Basic использовались разные средства обработки данных, причем выбор определялся типом источника данных. Например, обработка данных, прочитанных из файла на диске, принципиально отличалась от обработки данных, загруженных из Интернета. Эти времена остались в прошлом. Одной из целей, поставленных при проектировании .NET Framework, было обеспечение унифицированного механизма обработки данных, не зависящего от источника.

Центральное место в этом механизме занимает понятие потока (stream). Термин «поток» в данном случае происходит от выражения «поток байтов». Собственно, все данные, с которыми работает компьютер, — числа, текст и графика — сводятся к обычной последовательности байтов. Таким образом, подход, избранный проектировщиками .NET Framework, выглядит вполне логично — они разработали абстрактный класс, выполняющий обобщенные операции с данными. Наличие абстрактного класса упрощает программирование ввода-вывода в VB .NET и выявляет сходство между разнородными операциями. Короче говоря, абстрактный класс Stream, помеченный ключевым словом Mustlnherit, является идеальной базой для построения объектно-ориентированной иерархии ввода-вывода.

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

Глава завершается примером использования новых серверных средств RAD (Rapid Application Development) для написания простого монитора файловой системы. Программа следит за изменениями в каталогах (такими, как создание и удаление файлов) и обрабатывает различные события, инициируемые в зависимости от типа изменений. В предыдущих версиях VB написание подобных программ было сильно затруднено тем, что программисту приходилось использовать Windows API весьма нетривиальным образом. И хотя в этой книге мы не сможем сколько-нибудь полно описать RAD-иногоументарий VB .NET, мы надеемся, что это подстегнет ваше любопытство и заставит подробнее изучить этот чрезвычайно полезный аспект VB .NET.

Данная глава познакомит читателя с основными принципами ввода-вывода в .NET, однако она не претендует на полное изложение этой обширной темы. О вводе-выводе вполне можно написать отдельную книгу — как и серверном RAD-инструментарии!

Каталоги и файлы

В VB .NET существуют два класса для работы с каталогами и два класса для работы с файлами.

  • Классы Directory и Directorylnfo.
  • Классы File и Filelnfo.

Обращение к функциональным возможностям классов Directory и File происходит при помощи общих методов. Поскольку методы классов Di rectory и Fi1е являются общими, они могут вызываться и без предварительного создания экземпляра оператором New. Конечно, это повышает их эффективность при разовых обращениях к конкретному файлу или каталогу. Тем не менее при многократном обращении к файлу или каталогу эти методы становятся менее эффективными. Классы Di rectorylnfo и Filelnfo содержат обычные методы, поэтому обращение к их членам происходит через конкретные экземпляры.

Другое различие между этими парами заключается в том, что классы Directory и File являются производными непосредственно от Object, а классы Directory-Info и FileInfo объявлены производными от абстрактного (Mustlnherit) класса FileSystemInfo, содержащего универсальные методы вроде LastAccessTime и FullName.

И все же самое принципиальное различие состоит в другом. Классы Directorylnfo и Filelnfo гораздо лучше подходят для рекурсивного использования результатов, как было показано в примере, приведенном в главе 4. Дело в том, что члены классов Directory и File обычно возвращают строки с описанием каталогов или файлов, тогда как члены классов Di rectorylnfo и Filelnfo обычно возвращают экземпляры своих классов. Как было показано в главе 4, эта особенность упрощает написание рекурсивных программ.

Между этими парами существует еще одно тонкое различие: они обладают разными профилями безопасности. Хотя в этой книге нам не удастся сколько-нибудь подробно описать вопросы безопасности при программировании для .NET, вы должны хотя бы в общих чертах понимать, что классы Directory и File проверяют привилегии вашего кода для обращения или модификации файла или каталога при каждом использовании, а классы Directorylnfo и Filelnfo проверяют их всего один раз при создании экземпляра объекта. Именно этим и объясняется повышение их эффективности при многократном выполнении операций с одним файлом или каталогом.

Поскольку существование данных, к которым вы обращаетесь, не гарантировано, обращения к файлам или каталогам часто заключаются в блоки Try-Catch. Впрочем, на эти классы распространяется одно из основных правил при работе с исключениями: не используйте исключения там, где можно ограничиться простой проверкой. Например, в обычных условиях незачем перехватывать исключения Di rectoryNotFoundExcepti on — проще предварительно вызвать метод Exists и убедиться в том, что каталог существует. Ниже перечислены основные исключения, встречающиеся при операциях с файлами и каталогами. Иерархию возглавляет базовый класс IOException:

IOException

>DirectoryNotFoundException

> EndOfStreamException

>FileLoadException

>FileNotFoundException

Класс Path

Прежде чем рассматривать операции с каталогами и файлами, следует познакомиться с классом Path. Этот класс содержит несколько общих методов, предназначенных для обработки уточненных имен файлов [Любопытная подробность: в описании этого класса, приведением в документации VB .NET, упоминаются некоторые аспекты кросс-платформенных операций. В частности, упоминается о различиях между символом «/» и разделителем каталогов «», используемым в системах семейства UNIX (в том числе и в системе BSD, для которой Microsoft анонсировала поддержку CLR).]. Сетевые имена файлов устроены несколько сложнее локальных имен, поэтому методы класса Path приносят несомненную пользу (кстати говоря, анализ даже локальных имен — занятие на любителя). Основные члены класса Path перечислены в табл. 9.1.

Таблица 9.1. Важнейшие члены класса Path

Член класса

Описание

DirectorySeparatorChar

Символ-разделитель каталогов для текущей платформы

InvalidPathChars

Массив всех символов, недопустимых в уточненном имени файла

PathSeparator

Символ-разделитель компонентов уточненного имени файла для текущей платформы

VolumeSeparatorChar

Символ-разделитель имен томов для текущей платформы

ChangeExtension(ByVal path

As String,ByVaL extension As String)

Изменяет расширение файла и возвращает новое имя


GetDirectoryName (ByVal pathAs String)

Возвращает путь к каталогу, в котором находится файл

GetExtension(ByVal path As String)

Возвращает расширение файла

GetFHeName(ByVal path As String)

Возвращает имя и расширение для заданного уточненного имени

GetFullPath(ByVat path As String) Преобразует заданное имя файла в формат полного (fully qualified) имени
GetPathRoot(ByVal path As String) Возвращает корневой элемент заданного уточненного имени
GetTempFileName (ByVal path As String) Возвращает уникальное имя временного файла и создает на диске файл нулевой длины
GetTempPath(ByVal path As String) Возвращает путь к каталогу временных файлов в текущей системе

GetFileNameWithoutExtension (ByVal path As String)

Возвращает имя файла без расширения


Класс Directory

Большинство методов класса Directory идентифицирует каталоги при помощи возвращаемых строк. Поскольку все члены класса объявлены общими, при обращении к ним не обязательно указывать конкретный экземпляр. Пример:

System.IO.Directory.GetCurrentDirectory()

Эта команда возвращает строку с описанием текущего каталога. Метод GetDirectories(pathString) возвращает массив строк с описанием подкаталогов каталога, заданного параметром pathString. Описание интерпретируется либо как путь, заданный относительно каталога текущего приложения, либо как путь в схеме UNC (Universal Naming Convention). Следующая программа выводит имя текущего каталога и имена всех его подкаталогов.

Imports System.IO Module Modulel

Sub Main()

Dim curDir.nextDir As String Try

curDir =Directory.GetCurrentDirectory ()

Console.WriteLine(curDir)

For Each nextDir In Directory.GetDirectories(curDir)

Console.WriteLine(nextDir) Next

Catch ioe As IOException

Console.WriteLine("eeeks -i/o problems!" & ioe.message)

Catch e As Exception

Console.Write(e.stacktrace) Finally

Console.ReadLine()

End Try

End Sub

End Module

Если ваши потребности не ограничиваются простым выводом имен каталогов, лучше воспользоваться классом DirectoryInfo. Более подробное описание этого класса приводится ниже.

Помимо передачи строки с описанием каталога методу GetDirectories можно передать шаблон с метасимволами, используемыми в DOS [«?» обозначает один символ, а «*» — несколько символов.]. Важнейшие методы класса Di rectory перечислены в табл. 9.2. Во всех случаях параметры передаются по значению (с ключевым словом ByVal).

Таблица 9.2. Важнейшие методы класса Directory

Метод

Описание

Create Directory (ByVal pathName As String)

Создает каталог с заданным именем и возвращает объект Directory Info для созданного каталога. При необходимости также создаются все промежуточные каталоги

Delete(ByVal pathName As String)

Удаляет пустой каталог. Чтобы удалить непустой каталог вместе со всеми каталогами и файлами, воспользуйтесь командой Delete (pathName As String, True)

Exists(ByVal pathName As String)

Возвращает логический признак существования каталога

GetCreationTime (ByVal pathName As String)

Возвращает объект даты, содержащий информацию о дате и времени создания каталога

GetCurrentDi rectory

Возвращает строку с описанием текущего каталога

GetDirectories (ByVaL pathName As String)

Возвращает массив строк с описанием подкаталогов. При вызове может передаваться второй строковый параметр, содержащий шаблон

GetDi rectoryRoot •(ByVal pathName As String)

Возвращает строку с описанием корневой части заданного пути

GetFiles(ByVal pathName As String)

Возвращает массив строк с описаниями файлов каталога. При вызове может передаваться второй строковый параметр, содержащий шаблон

GetLastAccessTime (ByVal pathName As String)

Возвращает объект даты, содержащий информацию о времени последнего обращения к каталогу

GetLastWriteTime (ByVal pathName As String)

Возвращает объект даты, содержащий информацию о времени последней записи в каталог

GetLogicalDrives

Возвращает строковый массив с именами логических дисков в формате «диск:» (например, С:)

GetParent (ByVal pathName As String)

Возвращает строку с описанием каталога, родительского по отношению к заданному

Move(ByVal sourceDirName As String,ByVal destDirName As String)

Перемещает каталог со всем содержимым в пределах диска

SetCurrentDirectory (ByVal pathName As String)

Задает текущий каталог

 

Классе File

Класс File, как и класс Directory, состоит из общих методов, которым при вызове обычно передается имя файла. Эти методы применяктея при копировании, удалении и перемещении файлов. Основные методы класса File перечислены в табл. 9.3. Обратите внимание,— все параметры передаются по значению (в таблице отсутствуют методы класса File, предназначенные для работы с потоками данных, — они будут рассмотрены ниже).

Таблица 9.3. Основные методы класса File

Метод

Описание

Copy(ByVal sourceFiteName As String,ByVal destFileName As String)

Копирует файл. Существует перегруженная версия метода с третьим логическим параметром overwrite; если этот параметр равен True, существующий файл с заданным именем перезаписывается

Delete(ByVal path As String)

Удаляет заданный файл. Интересная подробность: если файл не существует, исключение не инициируется (см. описание метода Exists)

Exists(ByVal path As String)

Возвращает логическую величину, которая показывает, существует ли файл с заданным полным именем

GetAttributes(ByVal path As String)

Возвращает значение перечисляемого типа FileAttributes с описанием атрибутов файла — архивный, системный и т. д. (о том, как использовать полученный объект, рассказано в следующем разделе)

GetCreationTime (ByVal path As String)

Возвращает объект даты, содержащий информацию о времени создания файла

GetLastAccessTime (ByVal path As String)

Возвращает объект даты, содержащий информацию о времени последнего обращения к файлу

GetLastWriteTime (ByVal path As String)

Возвращает объект даты, содержащий информацию о времени последней записи в файл

Move(ByVal sourceFileName As String, ByVal destFileName As String)

Перемещает файл (поддерживается возможность перемещения на другой диск) и переименовывает его, если в параметре destFileName указано новое имя

SetAttributes(ByVal path As String, ByVal fileAttributes As FileAttributes)

Задает атрибуты указанного файла

 

Атрибуты файла

Операции с атрибутами файлов и каталогов выполняются достаточно часто, поэтому в .NET Framework был включен удобный класс FileAttri bute. Вероятно, правильнее было бы назвать его FileDi rectoryAttri bute, поскольку все атрибуты относятся не только к файлам, но и к каталогам.

Значения перечисляемого типа обычно объединяются поразрядными операциями, чтобы избежать всевозможных ошибок в программе. Не используйте команды следующего вида:

If File.GetAttributes("c:foo.txt") = FileAttributes.Readonly Then...

В проверяемом условии не учитывается тот факт, что у файла могут быть установлены и другие атрибуты. Правильная команда должна выглядеть так:

If File.GetAttributes("c:foo.txt") And FileAttributes.Readonly _

= FileAttributes.Readonly Then...

При необходимости атрибуты объединяются оператором Оr. Пример:

File.SetAttributes( "с: foo.txt".

Not (FileAttributes.Archive) Or FileAttributes.Hidden)

Команда назначает атрибуты C:foo.txt таким образом, что файл становится скрытым (Hidden), а архивный бит (Archive) сбрасывается. Ниже перечислены важнейшие значения этого перечисляемого типа:

Archive

Compressed

Di rectory

Encrypted

Hidden

Normal (атрибуты не установлены)

Readonly

System

Классы DirectoryInfo и FileInfo

В отличие от обобщенных классов Directory и Filе классы Directory Info и FileInfо инкапсулируют конкретные (или потенциально существующие) каталоги и файлы. Чтобы использовать их, необходимо предварительно создать экземпляр класса. Под потенциальным существованием мы имеем в виду, что объект Di rectorylnfo или Fi lelnfo может быть создан даже в том случае, если файл или каталог с заданным именем еще не существует и создается при последующем вызове метода Create.

Как правило, при создании экземпляров этих классов при вызове конструктора указывается имя каталога или файла. Пример:

Dim myDirectory As Directorylnfo

myDirectory = New Directorylnfo("C:Test Directory")

Текущий каталог обозначается символом «.»:

Dim currentDir As New Directorylnfo(".")

После создания объекта Directorylnfo можно запросить различные сведения о соответствующем каталоге — например, время создания:

MsgBox(myDirectory.GreatienTime)

Как упоминалось выше, одна из самых замечательных особенностей этих классов заключается в том, что их члены возвращают объекты, а не строки. Например, в следующей программе вызов GetFiles в выделенной строке возвращает коллекцию объектов Filelnfo, что позволяет при необходимости вызвать методы этих объектов.

Imports System.IO

Module Modulel Sub Main()

Dim myDi rectory As Directorylnfo Try

myDirectory =New DirectoryInfo("C:Test Directory")

Dim aFile As File Info

For Each aFile In myDirectory.GetFiles

Consol e. WriteLi ne( "The fi1e named " & aFile. Full Name & _

"has length " & aFile.Length) Next Catch e As Exception

MsgBox("eeks -an exception " & e.StackTrace) Finally

Console.WriteLine("Press enter to end")

Console.ReadLine()

End Try

End Sub

End Module

Рекурсивный просмотр дерева каталогов

Класс Directorylnfo удобен тем, что на его основе легко строятся обобщенные процедуры для рекурсивного перебора дерева каталогов. Как было показано в главе 4, при этом удобно использовать вспомогательную процедуру, которая, в свою очередь, вызывает другую процедуру для работы с файлами заданного каталога. Ниже приведена одна из возможных реализаций этого рекурсивного процесса:

Option Strict On Imports System.IO Module Modulel

SubMain()

Dim nameOfDirectory As String ="C:"

Dim myDirectory As DirectoryInfo

myDirectory = New DirectoryInfo(nameOfDirectory)

WorkWithDirectory(myDirectory)

End Sub

Public Sub WorkWithDirectory(ByVal aDir As Directorylnfo)

Dim nextDir As Directorylnfo WorkWithFilesInDir(aDir)

For Each nextDir In aDir.GetDirectories

WorkWithDirectory(nextDir) Next

End Sub

Public Sub WbrkWithFilesInDir(ByVal aDir As Directorylnfo)

Dim aFile As Filelnfo For Each aFile In aDir.GetFiles()

' Выполнить операцию с файлом.

' В нашем примере просто выводится уточненное имя.

Consolе.WriteLine(aFi1e.Ful1 Name) Next

End Sub

End Module

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

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

Private Sub Buttonl_Click(ByVal sender As System.Object,_

ByVal e As System.EventArgs) Handles Buttonl.Click

'Заменить курсор изображением песочных часов

Me.Cursor = Cursors.WaitCursor ListBoxl. Items. Clear()

WorkWithDirectory(New Directorylnfo(TextBoxl.Text))

Me.Cursor = Cursors.Default

End Sub

Public Sub WorkWithDirectory(ByVal aDir As Directorylnfo)

Dim nextDir As Directorylnfo Try

WorkWithFilesInDir(aDir)

For Each nextDir In aDir.GetDirectories

WorkWithDirectory(nextDi r) Next

Catch e As Exception

MsgBox(e.message SvbCrLf Se.StackTrace)

End Try

End Sub

Public Sub WorkWithFilesInDir(ByVal aDir As Directorylnfo)

Dim aFile As Filelnfo For Each aFile In aDir.GetFiles()

If aFile.Attributes And _

FileAttributes.Hidden = FileAttributes.Hidden Then

ListBoxl. Items. Add( "FOUND hidden filenamed " & aFile. FullName)

End If

Next

End Sub

Рис. 9.1. Форма для рекурсивного просмотра каталогов

Помимо мнврэпоточной реализации при более сложных операциях код процедуры WorkWithFHeslnDir следовало бы заключить в блок Try-Catch.

Важнейшие члены классов FileSystemInfo, FileInfo и DirectoryInfo

Класс FileSystemlnfo является базовым для классов Directorylnfo и Filelnfo и содержит большую часть их общей функциональности. Перед нами хороший пример тех возможностей, которые открываются при использовании абстрактных базовых классов. В классе Directory Info существует метод GetFileSystemlnfos, который возвращает массив объектов FileSystemlnfо, представляющих файлы и подкаталоги заданного каталога. Такое становится возможным только благодаря существованию класса FileSystemlnfo. Важнейшие члены базового класса FileSystemlnf о перечислены в табл. 9.4.

Таблица 9.4. Члены базового класса FileSystemInfo

Метод/свойство

Описание

Attributes (свойство)

Атрибуты объекта. Свойство доступно для чтения и записи

CreationTime (свойство)

Время создания объекта. Свойство доступно для чтения и записи

Exists (свойство)

Логический признак существования файла или каталога

Extension (свойство)

Расширение файла

FullName (свойство)

Полное имя каталога или файла

LastAccessTime (свойство)

Дата/время последнего обращения к объекту. Свойство доступно для чтения и записи

LastWriteTime (свойство)

Время последней записи в объект. Свойство доступно для чтения и записи

Name (свойство)

Для файлов — имя файла. Для каталогов — имя последнего каталога в иерархии, если это возможно. В противном случае возвращается полное имя

Delete

Удаляет объект

Refresh

Обновляет состояние объекта

В табл. 9.5 и 9.6 перечислены важнейшие методы класса DirectoryInfo и методы класса Filelnfo, не имеющие непосредственного отношения к потокам (эта тема будет рассматриваться позже).

Таблица 9.5. Основные методы класса DirectoryInfo

Метод/свойство

Описание

Exists (свойство) Логический признак существования каталога
Name (свойство) Имя каталога
Parent (свойство) Объект DirectoryInfo для родительского каталога (для корневых каталогов возвращается Nothing)
Create Создает каталог, путь к которому указан в конструкторе DirectoryInfo
CreateSubdirectory (ByVal As String) Создает подкаталог, путь к которому передается в виде параметра. Возвращает объект Directorylnfo для созданного подкаталога
Delete Удаляет пустой каталог, представленный объектом Directorylnfo. Если присвоить True необязательному логическому параметру, происходит рекурсивное удаление непустого каталога и всех его подкаталогов
GetDirectories Возвращает массив объектов Directorylnfo для подкаталогов текущего каталога
GetFiles

Возвращает массив объектов Filelnfo для файлов текущего каталога

GetFileSystemlnfos Хороший пример использования абстрактных классов: метод возвращает массив объектов FileSystemlnfo, представляющих все файлы и подкаталоги текущего каталога
MoveTo(ByVal destDirName As String) Перемещает Directorylnfo и все его содержимое
Root (свойство) Объект DirectoryIlnfo для корневого каталога в иерархии текущего каталога

 

Таблица 9.6. Члены класса Filelnfo, не возвращающие потоков

Метод/свойство

Описание

Directory (свойство) Объект Directorylnfo для каталога, в котором находится файл
DirectoryName (свойство) Полный путь к файлу в строковом виде
Exists (свойство) Логический признак существования файла
Length (свойство) Размер текущего файла

CopyTo(ByVal destFileName As String)

Копирует существующий файл и возвращает объект Filelnfo для копии. Необязательный логический параметр управляет перезаписью существующих файлов
Create Создает файл по имени, указанному при конструировании объекта Filelnfo, и возвращает объект FileSystem для нового файла
Delete Удаляет файл, представленный объектом FileInfo
MoveTo(ByVal destFileName As String) Перемещает файл

 

Идея выделения общей функциональности в абстрактный базовый класс выглядит впол-не логично, однако в данном случае она реализована не лучшим образом. Например, свдйство Length присутствует в файле FileInfo, но не поддерживается в FileSystemlnfo, поэтому для вычисления размера дерева каталогов приходится прибегать к услугам другого объекта — а именно вызывать метод Size объекта Folder, входящего в модель FileSystemObject. Эта модель впервые была представлена в VBScript, поэтому в решение приходится включать ссылку на библиотеку сценарной поддержки на базе СОМ.

Потоки данных

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

  1. Read — метод чтения данных из потока. Иногда сводится к простейшему чтению одного байта, но во многих производных классах используются сложные методы для чтения данных большими порциями.
  2. Write — метод записи данных в поток. Как и предыдущий метод, может сводиться к простейшей записи одного байта, но может задействовать и фрагменты данных большего размера.

Впрочем, этим возможности не ограничиваются. Кроме простого перемещения от первого байта к последнему реализация класса Stream может поддерживать и другие способы — например, перемещение в обратном направлении или непосредственный переход к заданной позиции в потоке. Такое возможно для файловых потоков, но не имеет смысла (а следовательно, и не реализуется) для потоков, основанных на сетевых соединениях. Свойство CanSeek позволяет узнать, поддерживает ли поток произвольный доступ. Если свойство равно True, значит, в производном классе поддерживаются реализации методов Seek и SetLength, а также свойств Position и Length.

В реализации метода Seek обычно используются три значения (Begin, Current и End), входящие в удобный перечисляемый тип SeekOrigin.

В табл. 9.7 перечислены основные методы абстрактного класса Stream, смысл которых должен сохраниться и в производных классах.

Таблица 9.7. Основные методы класса Stream

Метод/свойство

Описание

CanRead (свойство)

Логический признак поддержки чтения

CanSeek (свойство)

Логический признак поддержки произвольного доступа (позиционирования)

CanWrite (свойство)

Логический признак поддержки записи

Length (свойство)

Длина потока в байтах

Position (свойство)

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

Close

Закрывает поток и освобождает используемые ресурсы (например, файловые манипуляторы операционной системы)

Flush

Записывает данные и стирает содержимое всех буферов, используемых потоком

Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)

Читает заданное количество байтов начиная с текущей позиции с прибавлением заданного смещения offset. Возвращает количество успешно прочитанных байтов

Read Byte

Читает отдельный байт (почему-то в формате Integer) в текущей позиции потока. Если текущая позиция находится в конце потока, возвращает -1

Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)

Записывает заданное количество байтов начиная с текущей позиции с прибавлением заданного смещения offset

WriteByte(ByVal value As Byte)

Записывает байт в текущую позицию потока

Все классы иерархии Stream поддерживают метод Close, освобождающий удерживаемые ресурсы операционной системы (например, файловые манипуляторы или сетевые соединения), поэтому практически во всех программах, работающих с потоками, рекомендуется закрывать поток в блоке Try-Catch-Final 1у. Учтите, что вызов Close в секции Finally требует предварительной проверки, поскольку этот метод вызывается лишь для существующих объектов потоков, созданных успешным вызовом конструктора. Проверка перед вызовом Close в секции Final ly выглядит примерно так:

Finally

If Not (myFileStream Is Nothing) Then myFileStream.Close()

End Try

Рассмотрите и такую возможность, как реализация IDisposable в классах, выполняющих операции с файлами, и закрытие всех открытых потоков методом Dispose.

Основные классы, производные от Stream, перечислены в табл. 9.8.

Таблица 9.8. Основные классы, производные от Stream

Класс

Описание

FileStream Произвольный доступ к файлам
MemoryStream Представляет блок памяти (часто используется при работе с буферами)
NetworkStream Данные, полученные в виде потока по сетевому соединению. Принадлежит пространству имен System. Net. Sockets
CryptoStream Шифровка и расшифровка данных. Принадлежит пространству имен System. Security. Cryptography
BufferedStream «Оболочка» для поддержки буферизации в потоках, не обладающих этой возможностью (при использовании позволяет задать размер буфера). Например, автоматическая буферизация ввода используется в файловых потоках, но отсутствует в сетевых потоках. Если потребуется организовать буферизацию для сетевого потока, воспользуйтесь классом BufferedStream и методикой, описанной далее в этой главе

 

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

Запись в файл

Начнем с рассмотрения команды, часто встречающейся при работе с файловыми потоками:

Dim myFileStream As New FileStream("MyFile.txt". FileMode.OpenOrCreate, FileAccess.Write)

Как видно из приведенного фрагмента, эта версия конструктора FileStream получает имя файла (заданное по отношению к текущему каталогу, если не указано полное имя) и два параметра, значения которых относятся к перечисляемым типам FileMode и FileAccess соответственно. Таким образом, в нашем примере конструктор Fi1eStream либо создает файл с именем MyFile.txt в текущем каталоге, либо открывает его, если файл с таким именем уже существует. В любом случае программа сможет записывать данные в файл. Часто встречаются и другие конструкторы класса Fi leStream:

  • Sub New(String, FileMode): создает объект FileStream с заданным именем и в заданном режиме (см. ниже описание FileMode).
  • Sub NewCString, FileMode, FileAccess): создает объект FileStream в заданном режиме, с заданными правами чтения/записи и совместного доступа.

Допустимыми значениями перечисляемого типа FileAccesS являются Read, Write и ReadWri te. Основные значения перечисляемого типа Fi I eMode перечислены в табл. 9.9. Учтите, что некоторые из них требуют особых привилегий для операций с файлами.

Таблица 9.9. Значения перечисляемого типа FileMode

Значение

Описание

Append Открыть существующий файл (или создать несуществующий). Указатель текущей позиции перемещается в конец файла для записи. Используется совместно с FileAccess.Write
Create Создать новый файл. Внимание — существующий файл автоматически стирается!
CreateNew Создать новый файл. Отличается от Create тем, что для существующего файла инициируется исключение IOException
Open Открыть существующий файл. Если файл не существует, инициируется исключение IOException. Используется совместно с FileIOPermissionAccess.Read
OpenOrCreate Открыть или создать файл
Truncate Открыть существующий файл, удалить текущее содержимое

 

Объекты FHeStream также возвращаются следующими методами классов File и FHelnfo: File.Create, File.Open, File.OpenRead, File.OpenWrite, FHeInfo.Create, FHelnfo.Open, FHelnfo.OpenRead.

Хотя файловые потоки поддерживают произвольный доступ методом Seek, базовый класс-FileStream ориентирован исключительно на операции с байтами, поэтому его возможности ограничиваются простой записью байта или массива байтов методами WriteByte и Write. Приведенный ниже фрагмент создает файл, показанный на рис. 9.2:

Option Strict On Imports System.IO

Module Modulel

Sub Main()

Dim i As Integer

Dim theBytes(255) As Byte

For i = 0 To 255

theBytes(i) = CByte(i)

Next

Dim myFileStream As FileStream

Try

myFileStream = New FileStream("C:foo",

Fi1eMode.OpenOrCreate. FileAccess.Write)

myFlleStream.Write(theBytes, 0. 256) Finally

If Not (myFileStream Is Nothing) Then

myFileStream.Close()

End Try

DisplayAFile("C:foo")

End Sub

End Module

Рис. 9.2. Запись двоичных данных в файл

После выполнения этого фрагмента записанные данные можно прочитать методом Read, а также воспользоваться методом Seek для перехода к произвольной позиции в файле. Впрочем, как это всегда бывает при работе с неструктурированными потоками байтов, вам придется самостоятельно преобразовать двоичные данные в более полезный формат. В результате сейчас трудно найти более содержательный пример, чем простой вывод записанных чисел процедурой, приведенной ниже:

Sub ReadDataBack()

Dim myFileStream As Stream.i As Integer Try

myFileStream = New FileStream("C:foo",

FileMode.Open. FileAccess.Read)

For i = 0 To 255

Console.Write(myFileStream.ReadByte) Next

Catch e As Exception MsgBox(e.Message)

Finally

If Not (myFileStream Is Nothing) Then

myFileStream.Close()

End Try

End Sub

Метод Length базового класса Stream всегда позволяет прочитать нужное количество байтов в цикле независимо от структуры файла. Например, следующая процедура читает файл по одному байту. Обнаруженные исключения просто передаются вызывающей стороне; вероятно, в реальной программе следовало бы определить новый класс исключения:

Sub DisplayAFile(ByVal theFileName As String)

Dim theFile As FileStream

Dim i As Long Try

theFile = New FileStream(theFileName.

Fi1eMode.Open,Fi1eAccess.Read)

For i = 0 To (theFile.Length - 1)

' Вычесть 1. поскольку отсчет начинается с 0

Consolе.Write(theFiIe.ReadByte) Next

Catch Throw Finally

If Not (theFile Is Nothing) Then theFile.Close()

End Try

End Sub

Если файл имеет небольшие размеры и легко помещается в памяти, воспользуйтесь одним вызовом Read и прочитайте весь файл в байтовый массив нужного размера. Такая операция выполняется значительно быстрее.

Другой распространенный вариант посимвольного чтения основан на том, что метод ReadByte в конце потока возвращает -1. Основной цикл выглядит примерно так:

Dim i As Integer i = theFile.ReadByte

Do Until i =-1

Console.Write(i)

i = theFile.ReadByte Loop

Чтение/запись файла на уровне отдельных байтов используется не так уж часто; в основном это необходимо при выполнении низкоуровневых операций. При операциях более высокого уровня часто используется стандартный прием — неструктурированный файловый поток передается конструктору потока, обладающего более широкими возможностями. Этот принцип называется многоуровневой организацией потоков. Например, неструктурированный файловый поток можно передать потоку, автоматически распознающему текст. Разные способы многоуровневой организации потоков описаны в нескольких ближайших разделах. Но прежде, чем переходить к этим разделам, просмотрите табл. 9.10 — в ней перечислены основные методы и свойства базового класса FileStream. В дальнейшем мы будем использовать эти методы, хотя базовый файловый поток будет скрыт потоками более высоких уровней.

Таблица 9.10. Основные члены класса RleStream

Метод/свойство

Описание

Handle (свойство)

Файловый манипулятор операционной системы для файла, инкапсулированного в объекте FileStream. Свойство доступно для чтения

Length (свойство)

Размер потока в байтах. Свойство доступно для чтения

Name (свойство)

Уточненное имя, переданное конструктору FileStream

Position (свойство)

Текущая позиция для операций чтения или записи в потоке (нумерация позиций начинается с нуля). Свойство доступно для чтения и записи

Close

Закрывает поток и освобождает все связанные с ним ресурсы

Flush

Пересылает все данные из буфера в устройство. Автоматически вызывается при вызове Close

Lock(ByVal position As Long,

ByVal length As Long)