Ввод-вывод

В прежних версиях 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 и 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

Consol e. 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)

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