Важнейшие члены классов 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:

Допустимыми значениями перечисляемого типа 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)

Блокирует доступ ко всему файлу или его части со стороны других

процессов (нумерация позиций начинается с нуля)

Read(ByVal array() As Byte,

ByVal offset As Integer,

ByVal count As Integer)

Читает заданное количество байтов в массив из файлового

потока начиная с заданной позиции


ReadByte


Читает один байт из файла и перемещает указатель текущей

позиции на один байт вперед

Seek(ByVal offset As Long,

ByVal origin As SeekOrigin)

Устанавливает указатель текущей позиции в потоке в заданное

положение

Unlock(ByVal position As Long,

ByVal length As Long)

Снимает блокировку с ранее заблокированной части файла

(нумерация позиций начинается с нуля)

Write(ByVal array() As Byte,

ByVal offset As Integer,

ByVal count As Integer)

Записывает заданное количество байт из массива в файловый

поток начиная с заданной позиции


WriteByte

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