Лабораторная работа №10

Основы работы с файлами в C #

Работа с файлами в языке C # реализуется посредством потоков. Поток – это абстрактное понятие, описывающее любой перенос данных от источника к приемнику. Именно потоки позволяют считывать данные из файла и записывать данные в файл. Поток представляет собой последовательность байтов и не зависит от конкретного устройства, с которым производится обмен (оперативная память, файл на диске, клавиатура, принтер).

Для поддержки потоков библиотека .NET содержит иерархию классов, основная часть которой представлена на рис. 10.1.

Отметим, что эти классы определены в пространстве имен System.IO, поэтому чтобы воспользоваться объектами и методами этих классов, необходимо указать пространство имен System.IO в области директивы using в начале программы.

Рассмотрим некоторые из перечисленных выше классов, необходимые для организации работы с фалами, подробнее.

Классы TextReader и StreamReader

В языке C # такие операции как считывание данных из файла и запись данных в файл реализованы на основе манипуляций с байтами. Однако в связи с тем, что человеку гораздо более удобно воспринимать информацию, представленную в символьном формате, в библиотеке .NET разработаны классы StreamReader и StreamWriter, которые позволяют преобразовывать байтовые потоки в символьные и наоборот.

Как видно из рисунка 10.1,класс StreamReader является потомком абстрактного базового класса TextReader, предоставляющего классам-наследникам набор методов, реализующих считывание последовательности символов или строк из потока (файла). Некоторые из методов приведены в таблице 10.1.

Таблица 10.1

Некоторые методы класса TextReader

Название Описание
Peek () Возвращает следующий символ из потока, при этом указатель текущей позиции не перемещается (удобно использовать для проверки наличия следующего символа в потоке: если конец файла достигнут, возвращаемое значение равно –1)
Read () Первый вариант: считывает текущий символ из потока и передвигает указатель на один символ в соответствии с кодировкой. Если доступных для считывания символов нет, возвращает значение –1. Второй вариант: считывает из потока последовательность символов и записывает ее в массив символов, начиная с определенного индекса. Метод возвращает фактическое количество считанных символов, либо нуль при отсутствии символов, доступных для чтения
ReadBlock () Блокирующая версия метода Read ()
ReadLine () Считывает строку символов из текущего потока и возвращает данные в строковом формате либо значение null, если достигнут конец потока входных данных
ReadToEnd () Считывает все символы, начиная с текущей позиции, до конца потока как одну строку

Поясним разницу между блокирующими и неблокирующими методами чтения. Обмен с потоком для повышения скорости передачи данных производится, как правило, через специальную область оперативной памяти, называемую буфер. Буфер выделяется для каждого открытого файла. При чтении из файла данные вначале считываются в буфер, причем не столько, сколько запрашивается, а сколько помещается в буфер, а уже из буфера передаются в поток. Таким образом, в процессе считывания, возможна ситуация, когда буфер пуст, т. е. данные пока не доступны, но ожидаемы в будущем. Именно в такой ситуации блокирующие и неблокирующие методы ведут себя по-разному: первые блокируют процесс считывания, помещая его в режим сна до тех пор, пока не появятся доступные для чтения данные, в то время как вторые просто возвращают в процесс некоторое значение, указывающее на то, что доступных для чтения данных нет.

Для того чтобы получить доступ к любому из описанных выше методов, необходимо создать экземпляр класса StreamReader с помощью конструктора StreamReader(<ИмяФайла>), при этом создается экземпляр класса StreamReader и связывается с конкретным физическим файлом. В качестве параметра можно указать либо только имя файла и в этом случае файл должен находиться в папке “…\ bin \ debug ” текущего проекта, либо полностью путь и имя файла. Пример использования:

try

{

StreamReader InputFile = new StreamReader("input.txt");

while (InputFile.Peek()!= -1)

Console.Write((char)InputFile.Read());

InputFile.Close();

}

catch

{

Console.WriteLine("Ошибка чтения файла");

}

Здесь мы создаем экземпляр класса StreamReader и связываем его с файлом “ input. txt ”. Далее в цикле while считываем посимвольно данные из файла с помощью метода Read () и выводим их на экран. Обратите внимание на то, что метод Read () возвращает значение считанного символа в формате int, поэтому мы преобразуем его в символьный формат. После того как конец файла достигнут, необходимо освободить ресурсы памяти, выделенные потоку, с помощью метода Close (). Оператор try позволяет обработать ошибку, которая может возникнуть при работе с файлом, например, в случае если файл не найден.

Напомним также о необходимости указать пространство имен System. IO в области директивы using в начале программы, иначе компилятор не сможет распознать встроенный класс StreamReader.

Для того чтобы проверить работоспособность программы, необходимо создать текстовый файл ” input. txt ” и сохранить его в папке “…\ bin \ debug ” текущего проекта. Это можно сделать в любом текстовом редакторе, но удобнее – непосредственно в среде Visual Studio. Для этого нужно выбрать пункт меню FileNewFile …, в открывшемся диалоговом окне создания нового файла выбрать тип “ Text File ”, а далее ввести в файл данные и сохранить его.

Отметим, что альтернативой методу Close () может служить использование конструкции using, в этом случае занимаемая потоком память освобождается автоматически при выходе из конструкции. Пример:

try

{

using (StreamReader InputFile = new

StreamReader(@"c:\work\input.txt"))

while (InputFile.Peek()!= -1)

Console.WriteLine(InputFile.ReadLine());

}

catch

{

Console.WriteLine("Ошибка чтения файла");

}

Здесь мы считываем данные из файла построчно и выводим их на экран. Обратите внимание, что путь к файлу указан полностью, при этом символ ‘@’ позволяет блокировать управляющие символы.

Отдельно рассмотрим в качестве примера заполнение одномерного и двумерного массивов из файла.

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

Итак, опишем метод ReadFromFile (), реализующий заполнение целочисленного одномерного массива MyArray из заданного файла. Исходя из условия задачи, в качестве параметров данного метода введем, во-первых, имя файла FileName, во-вторых, одномерный массив MyArray, и, наконец, фактическую размерность массива ArraySize, определяемую в теле метода.

static void ReadFromFile(string FileName, int[] Arr, ref int ArrSize)

{

try

{

using (StreamReader InputFile = new StreamReader(@FileName))

{

ArrSize = 0;

while (InputFile.Peek()!= -1)

{

Arr[ArrSize] =

int.Parse(InputFile.ReadLine());

ArrSize++;

}

}

}

catch

{

Console.WriteLine("Ошибка чтения файла");

}

}

В основной программе необходимо описать и проинициализировать одномерный массив с учетом максимально допустимой размерности массива MaxArraySize и вызвать описанный выше метод ReadFromFile ().

static void Main()

{

const int MaxArraySize = 1000;

int[] MyArray = new int[MaxArraySize];

int ArraySize=0;

ReadFromFile("input.txt", MyArray, ref ArraySize);

}

Прежде чем запустить программу, необходимо создать текстовый файл “ input. txt ”, заполнить его целыми числами и сохранить в папке “…\ bin \ debug ” текущего проекта. Важно отметить, что в программе используется построчное считывание данных с помощью метода ReadLine (), поэтому данные во входном файле должны быть введены не в одну строчку, а в столбец (см. рис. 10.2).

При выполнении этого программного кода мы указываем максимальный размер массива MaxArraySize, но это имеет два недостатка. Во-первых, если указать максимальный размер меньше, чем действительное количество элементов в файле, то произойдет ошибка. Во-вторых, если указывать слишком большое значение MaxArraySize, то будет выделяться место в памяти под элементы массива, которые никогда не будут использоваться.

Из этой ситуации имеется такой выход: сначала прочитать файл и узнать, сколько действительно элементов он содержит, а уже потом выделять место в памяти под элементы массива. Метод Main () в таком случае содержит операторы:

int[] MyArray;

ReadFromFile("input.txt", out MyArray);

Метод ReadFromFile () изменяется так:

static void ReadFromFile(string FileName, out int[] MyArray)

{

try

{

int ArraySize = 0;

using (StreamReader InputFile = new StreamReader(@FileName))

{ //подсчет количества элементов в файле

while (InputFile.Peek()!= -1)

{

InputFile.ReadLine();

ArraySize++;

}

}

//выделение памяти под необходимое число элементов

MyArray = new int[ArraySize];

using (StreamReader InputFile = new StreamReader(@FileName))

{ //инициализация массива

for (int i = 0; i < ArraySize; i++)

MyArray[i] = int.Parse(InputFile.ReadLine());

}

}

catch

{

Console.WriteLine("Ошибка чтения файла");

MyArray = null;

}

}

Заметьте, что массив передается как выходной параметр с использованием ключевого слова out. Поскольку нет гарантии, что операторы в контролируемом блоке try выполнятся без ошибок, блок catch должен содержать оператор, инициализирующий выходной параметр – массив (мы присваиваем значение null).

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

Альтернативой предложенному варианту решения является использование метода ReadAllLines () класса File, который считывает все строки файла в массив строк. В этом случае второй проход по файлу не потребуется.

При заполнении двумерного массива воспользуемся таким способом (его можно применить и при работе с одномерным массивом): фактическую размерность массива, т. е. количество строк и столбцов, зададим в начале входного файла, а далее укажем значения его элементов (см. рис. 10.3), поэтому в списке параметров метода ReadFromFile () при передаче в метод двумерного массива MyArray необходимо также указать директиву out, позволяющую инициализировать массив не в основной программе, а непосредственно в теле метода, после считывания его фактической размерности из файла. Отметим, что процесс считывания данных осуществляется построчно с помощью метода ReadLine (). Считанная строка разбивается с помощью метода Split () на массив строк Row. Элементы этого массива переписываются в результирующую матрицу MyArray, преобразуясь из строк в целые числа.

static void ReadFromFile(string FileName, out int[,] MyArray)

{

try

{

using (StreamReader InputFile = new StreamReader(FileName))

{

int RowsCount = int.Parse(InputFile.ReadLine());

int ColsCount = int.Parse(InputFile.ReadLine());

MyArray = new int[RowsCount, ColsCount];

for (int i = 0; i < RowsCount; i++)

{

string[] Row = InputFile.ReadLine().Split();

for (int j = 0; j < ColsCount; j++)

MyArray[i, j] = int.Parse(Row[j]);

}

}

}

catch

{

Console.WriteLine("Ошибка чтения файла");

MyArray = null;

}

}

static void Main(string[] args)

{

int[,] MyArray;

ReadFromFile("input.txt", out MyArray);

}

Классы TextWriter и StreamWriter

Абстрактный класс TextWriter содержит методы, позволяющие производить запись символов в байтовый поток (файл). Основные методы приведены в таблице 10.2.

Таблица 10.2

Методы класса TextWriter

Название Описание
Close () Закрывает файл, освобождает связанные с ним ресурсы
Write () Осуществляет запись данных в текстовый поток. Метод позволяет принимать в качестве параметра данные, представленные в разных форматах (целые или вещественные числа, символы или массивы символов, строки, логические переменные)
WriteLine () Записывает в текстовый поток данные, за которыми следует признак конца строки

Класс StreamWriter является классом-наследником класса TextWriter и реализует методы класса-предка.

Конструктор StreamWriter(<ИмяФайла>) создает текстовый поток для записи данных в заданный файл. Отметим, что имя файла может быть задано с указанием пути либо без него. Если файл существует, он будет перезаписан, в противном случае создается новый файл.

Рассмотрим использованием описанных методов на простом примере: вывести значения одномерного массива в файл. Напишем метод WriteToFile (), реализующий вывод элементов массива в заданный файл. Исходя из условия задачи, метод будет принимать в качестве параметров, во-первых, имя заданного файла FileName, во-вторых, одномерный массив MyArray.

static void WriteToFile(string FileName, int[] MyArray)

{

using (StreamWriter OutputFile = new StreamWriter(FileName))

{

foreach (int element in MyArray)

OutputFile.Write("{0} ", element);

}

}

Здесь мы создаем экземпляр класса StreamWriter с именем OutputFile для заданного файла, в цикле foreach последовательно просматриваем все элементы заданного массива и записываем их в файл с помощью метода Write ().

Основная программа включает в себя описание массива с инициализацией и вызов метода WriteToFile ().

static void Main(string[] args)

{

int[] MyArray = {1, 22, 13, -4, 5, 16, 27, 18, -9};

WriteToFile("output.txt", MyArray);

}

Отметим, что в рассмотренном примере значения одномерного целочисленного массива MyArray будут записаны в файл через пробел. Имя файла задано без указания пути к файлу, поэтому, чтобы просмотреть результаты работы программы, необходимо открыть файл “ output. txt ”, который находится в каталоге “…\ bin \ debug \” текущего решения.

Классы Stream и FileStream

Класс Stream является абстрактным базовым классом всех потоков. При работе с потоком допустимы три основные операции:

– чтение из потока – перенос информации из потока в структуру данных, такую как массив байтов;

– запись в поток – передача данных из структуры данных в поток;

– поиск, включающий в себя отправку запросов и изменение текущей позиции внутри потока.

Некоторые свойства и методы класса Stream приведены в таблице 10.3.

Таблица 10.3

Свойства и методы класса Stream

Название Вид Описание
CanRead неизменяемое свойство Показывает, можно ли ввести данные из потока
CanWrite неизменяемое свойство Показывает, можно ли записать данные в поток
CanSeek неизменяемое свойство Показывает, поддерживает ли поток произвольный доступ к данным, при котором можно задать текущее положение в потоке
Close () экземплярный метод Закрывает текущий поток и освобождает все связанные с ним ресурсы
Flush () экземплярный метод Записывает данные из буфера в связанный с потоком источник данных (файл) и очищает буфер
Length неизменяемое свойство Возвращает длину потока в байтах
Position изменяемое свойство Возвращает либо задает текущее положение в потоке
Read () экземплярный метод Считывает последовательность байтов определенной длины из потока и записывает их в одномерный массив байт. Возвращает количество прочитанных байтов либо ноль, если достигнут конец потока
ReadByte () экземплярный метод Возвращает целочисленное представление следующего байта из потока либо –1, если достигнут конец потока
ReadTimeOut изменяемое свойство Возвращает или задает продолжительность времени ожидания в миллисекундах в процессе чтения данных из потока
Seek () экземплярный метод Перемещает указатель текущего положения в потоке на заданную позицию
Write () экземплярный метод Записывает последовательность байт определенной длины в массив, начиная с указанного индекса, и перемещает указатель в потоке на количество записанных байт
WriteByte () экземплярный метод Записывает один байт в поток и перемещает указатель в потоке на один байт
WriteTimeOut изменяемое свойство Возвращает или задает продолжительность времени ожидания в миллисекундах в процессе записи данных в поток

Ввод/вывод, организованный на уровне байтов, называется байтовым потоком. Для создания байтового потока служит класс FileStream, являющийся классом-наследником класса Stream и реализующий рассмотренные выше методы и свойства. Конструктор класса перегружен и имеет несколько вариантов реализации, мы рассмотрим только одну из них. FileStream(<ИмяФайла>,<Режим>,<ТипДоступа>)- создает экземпляр класса FileStream, связывает его с заданным файлом и открывает файл в заданном режиме с заданным типом доступа. Поясним, что любой файл можно открывать в различных режимах. При этом режим открытия файла задается одним из константных значений перечисления FileMode:

Append – указывает, что операционная система (ОС) должна открыть существующий файл для записи, при этом текущий указатель положения в файле устанавливается в конец файла. Если файл не существует, создает новый файл;

Create – указывает, что ОС должна создать новый файл. Если в каталоге уже существует файл с заданным именем, он будет переписан;

CreateNew – указывает, что ОС должна создать новый файл. Если в каталоге уже существует файл с заданным именем, генерируется исключение IOException;

Open – указывает, что ОС должна открыть существующий файл. Если файл с заданным именем не существует, генерируется исключение FileNotFoudException;

OpenOrCreate – указывает, что ОС должна открыть файл с заданным именем, если он существует, либо создать файл с заданным именем в противном случае;

Truncate – указывает, что ОС должна открыть существующий файл. После открытия он должен быть обрезан до нулевой длины. Файл может быть открыт только для записи.

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

Read – указывает, что файл открыт только для чтения;

Write – указывает, что файл открыт только для записи;

ReadWrite – указывает, что файл доступен и для чтения и для записи.

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

try

{

FileStream InputFile = new FileStream("input.txt", FileMode.Create, FileAccess.Write);

for (byte i = 0; i < 100; i++)

InputFile.WriteByte(i);

InputFile.Close();

}

catch

{

Console.WriteLine("Ошибка ввода");

}

Здесь мы создаем файл “ input. txt ” и открываем его для записи. Далее записываем в него целые числа от 0 до 99. Важно заметить, что при открытии созданного текстового файла вы не увидите числа от 0 до 99. Это связано с тем, что данные записаны в файл на уровне байт, и не преобразованы в символьный формат, удобный для восприятия человеком.

В следующем примере мы открываем созданный файл “ input. txt ” для чтения и выводим считанные данные на экран.

try

{

FileStream InputFile = new FileStream("input.txt",

FileMode.Open, FileAccess.Read);

int b = InputFile.ReadByte();

while (b!= -1)

{

Console.WriteLine(b);

b = InputFile.ReadByte();

}

InputFile.Close();

}

catch

{

Console.WriteLine("Ошибка чтения файла");

}

В результате работы программы на экран будет выведена последовательность чисел от 0 до 99.

Используя метод Seek () для позиционирования указателя текущего положения в файле, можно управлять процессом считывания данных из файла. Синтаксис: Seek(long offset, SeekOrigin origin). Здесь параметр offset задает смещение в байтах относительно точки отсчета положения в потоке. Точка отсчета задается параметром origin и может принимать одно из константных значений перечисления SeekOrigin: Begin – начало файла, Current – текущее положение, End – конец файла.

Следующая модификация рассмотренного выше примера, используя метод Seek (), считывает лишь каждый третий байт из потока.

try

{

FileStream InputFile = new FileStream("input.txt",

FileMode.Open, FileAccess.Read);

int b = InputFile.ReadByte();

while (b!= -1)

{

Console.WriteLine(b);

InputFile.Seek(2, SeekOrigin.Current);

b = InputFile.ReadByte();

}

InputFile.Close();

}

catch

{

Console.WriteLine("Ошибка чтения файла");

}

Подчеркнем, что байтовый поток удобно использовать только для решения задач, в которых операции с файлами реализуются программным путем и пользователю не требуется самому изменять содержимое файлов. Если же необходимо считать из файла данные, введенные пользователем и представленные в символьном формате, удобнее пользоваться классами StreamReader и StreamWriter. Напомним, что эти классы, по сути, являются лишь надстройкой над классом Stream, они тоже используют байтовые потоки для чтения данных из файла и записи данных в файл, но при этом выполняют автоматическое преобразование байтового потока в символьный и наоборот (рис. 10.4).

 
 
Рис. 10.4. Байтовый и символьный потоки


Классы BinaryReader и BinaryWriter

Кроме байтовых и символьных потоков в языке C # реализованы двоичные потоки для работы с примитивными типами данных, такими как int, double, short и др. Двоичные потоки хранят значения в двоичном коде и не предназначены для просмотра их человеком.

Двоичный поток открывается на основе базового потока, который, в частности может быть инициализирован с помощью класса FileStream.

Так конструктор BinaryReader(<ИмяБазовогоПотока>) открывает двоичный поток для считывания данных из файла, ассоциированного с заданным базовым потоком.

Некоторые свойства и методы класса BinaryReader приведены в таблице 5.

Таблица 10.5

Свойства и методы класса BinaryReader

Название Вид Описание
BaseStream неизменяемое свойство Предоставляет доступ к базовому потоку
Close () экземплярный метод Закрывает текущий и связанный с ним базовый поток
PeekChar () экземплярный метод Возвращает следующий доступный для чтения символ в формате int, не перемещая внутренний указатель текущего положения в потоке. Если доступного для чтения символа нет, возвращает значение –1
Read () экземплярный метод Считывает текущий символ из базового потока и перемещает внутренний указатель потока на один символ, либо возвращает значение –1, если доступных для чтения символов нет

Метод Read () перегружен и имеет несколько вариантов реализации. Так, его можно использовать для считывания из базового потока последовательности байтов определенной длины и записи их в одномерный массив с указанного индекса; метод при этом возвращает количество считанный из потока байт, либо ноль, если достигнут конец потока. Также метод Read () может считывать из базового потока последовательность символов.

Кроме того, в классе BinaryReader определены методы, реализующие считывание из базового потока значений всех встроенных в C # примитивных типов. Синтаксис таких методов одинаков и имеет вид: ReadXX (), где вместо XX указывается соответствующий тип данных. Например, метод ReadBoolean () считывает из базового потока величину типа Boolean (bool) и перемещает внутренний указатель потока на соответствующее количество байт вперед в зависимости от типа считанной величины, в данном случае на один байт.

Конструктор BinaryWriter(<ИмяБазовогоПотока>) открывает двоичный поток для записи данных в файл, ассоциированный с заданным базовым потоком. Основные методы приведены в таблице 10.6.

Таблица 10.6

Методы класса BinaryWriter

Название Описание
Flush () Записывает данные из буфера в связанный с потоком источник данных (файл) и очищает буфер
Seek () Устанавливает позицию указателя в текущем потоке (имеет тот же синтаксис, что и аналогичный метод класса Stream)
Write () Записывает заданное значение в текущий поток
Close () Закрывает текущий и связанный с ним базовый поток

Приведем пример записи в файл и чтения из файла данных разных типов с использованием двоичных потоков.

try

{

FileStream MyFile = new FileStream("input.txt", FileMode.Create,

FileAccess.Write);

BinaryWriter MyBinaryFile = new BinaryWriter(MyFile);

MyBinaryFile.Write("Ivanov");

MyBinaryFile.Write("PM-11");

MyBinaryFile.Write(1);

MyBinaryFile.Write(true);

MyBinaryFile.Write("Petrov");

MyBinaryFile.Write("PM-21");

MyBinaryFile.Write(2);

MyBinaryFile.Write(false);

MyBinaryFile.Close();

}

catch

{

Console.WriteLine("Ошибка ввода");

}

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

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

try

{

FileStream MyFile = new FileStream("input.txt", FileMode.Open,

FileAccess.Read);

BinaryReader MyBinaryFile = new BinaryReader(MyFile);

Console.Write("Name: " + MyBinaryFile.ReadString());

Console.Write("\tGroup: " + MyBinaryFile.ReadString());

Console.Write("\tCourse: " + MyBinaryFile.ReadInt32());

Console.WriteLine("\tLiving in a hostel: " +

MyBinaryFile.ReadBoolean());

Console.Write("Name: " + MyBinaryFile.ReadString());

Console.Write("\tGroup: " + MyBinaryFile.ReadString());

Console.Write("\tCourse: " + MyBinaryFile.ReadInt32());

Console.WriteLine("\tLiving in a hostel: " +

MyBinaryFile.ReadBoolean());

MyBinaryFile.Close();

}

catch

{

Console.WriteLine("Ошибка ввода");

}

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

Классы FileInfo и DirectoryInfo

Методы работы с файлами не ограничиваются только манипуляциями с данными в процессе ввода/вывода, а включают в себя также такие операции как копирование, перемещение, удаление, вывод информации об объекте файловой системы – файле или каталоге. Для реализации этих операций в языке C # предусмотрены следующие классы:

– статический класс Directory и экземплярный класс DirectoryInfo позволяют создавать, копировать, перемещать, переименовывать и удалять каталоги, а также получать и задавать сведения о типе DateTime, относящиеся к созданию, доступу и записи каталога;

– статический класс File и экземплярный класс FileInfo позволяют создавать, копировать, удалять, перемещать и открывать файлы, а также может использоваться при инициализации объектов FileStream.

В таблице 10.7 приведены некоторые свойства и методы класса FileSystemInfo, являющегося базовым для классов FileInfo и DirectoryInfo (см. рис. 10.1).

Таблица 10.7

Свойства и методы класса FileSystemInfo

Название Вид Описание
Attributes изменяемое свойство Возвращает или устанавливает атрибуты для данного объекта файловой системы. Использует константные значения перечисления FileAttributes
CreationTime изменяемое свойство Получает или задает время создания объекта файловой системы
Delete () экземплярный метод Удаляет текущий объект файловой системы – файл или каталог – без возможности его восстановления
Exists неизменяемое свойство Определяет, существует ли данный объект файловой системы
Extension неизменяемое свойство Возвращает расширение файла в строковом формате
FullName неизменяемое свойство Возвращает имя файла или каталога с указанием полного пути
LastAccessTime изменяемое свойство Позволяет получить или задать время последнего обращения к объекту файловой системы
LastWriteTime изменяемое свойство Позволяет получить или установить время последней операции записи в объект файловой системы
Name неизменяемое свойство Для файлов возвращает имя файла, для каталогов – имя последнего каталога в иерархии, если таковая существует, а если нет, возвращает имя текущего каталога
Refresh () экземплярный метод Обновляет состояние объекта. Используется перед попыткой получения сведений об атрибуте текущего объекта, иначе сведения могут оказаться устаревшими

Как уже было сказано, описанные свойства и методы реализуются классами-наследниками FileInfo и DirectoryInfo.

Конструктор класса FileInfo(<ИмяФайла>) создает экземпляр класса и ассоциирует его с заданным файлом. Рассмотрим некоторые его методы (таблица 10.8).

Таблица 10.8

Методы класса FileInfo

Название Описание
CopyTo () Первый вариант: если в параметрах указано только имя файла, то копирует текущий файл в новый файл, имя которого задано в качестве параметра. В случае если создаваемый файл уже существует, его перезапись недопустима. Второй вариант: если кроме имени файла указан логический параметр, метод копирует текущий файл в новый файл и позволяет разрешить или запретить перезапись файла
Create () Создает файл и инициализирует байтовый поток класса FileStream, связанный с заданным файлом
MoveTo () Перемещает текущий файл в новое местоположение и разрешает переименование файла

Конструктор DirectoryInfo(<Путь>) выполняет инициализацию нового экземпляра класса для заданного пути. Рассмотрим некоторые его методы (таблица 10.10).

Таблица 10.10

Методы класса DirectoryInfo

Название Описание
Create () Создает каталог, заданный в конструкторе класса. Если такой каталог уже существует, метод ничего не выполняет
CreateSubDirectory () Создает один или несколько подкаталогов по заданному пути
GetDirectories () Возвращает подкаталоги текущего каталога в виде массива объектов класса DirectoryInfo
GetFiles () Первый вариант: позволяет получить файлы в текущем каталоге в виде массива объектов класса FileInfo. Второй вариант: если указано имя файла, метод позволяет осуществить поиск всех файлов, имена которых удовлетворяют некоторому условию поиска, заданному в строковом формате, и возвращает массив объектов класса FileInfo, либо null если таких файлов нет. При указании в параметре имени файла, разрешено использовать стандартные подстановочные знаки ‘*’ и ‘?’, используемые при поиске
MoveTo () Перемещает каталог и все его содержимое в местоположение, на которое указывает новый путь

Кроме методов, у класса DirectoryInfo есть свойство Parent, которое возвращает родительский каталог текущего подкаталога, либо значение null, если путь к файлу пуст или указывает на корневой каталог.

Рассмотрим использование описанных методов и свойств классов FileInfo и DirectoryInfo на примере решения следующей задачи: удалить все файлы с заданным расширением из заданного каталога, включая подкаталоги.

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

Console.WriteLine("Введите имя каталога");

string DirName = Console.ReadLine();

Console.WriteLine("Введите расширение файлов, которые надо удалить");

string FileName = Console.ReadLine();

Далее опишем метод DeleteFiles () следующим образом:

static void DeleteFiles(string DirName, string FileName)

{

try

{

DirectoryInfo MyDir = new DirectoryInfo(@DirName);

if (!MyDir.Exists) //проверка существования каталога

{

Console.WriteLine("Каталог " + MyDir.Name +

" не существует");

return;

}

DelFiles(MyDir, FileName);

Console.WriteLine("Удаление завершено");

}

catch

{

Console.WriteLine("Ошибка");

}

}

Здесь мы инициализируем экземпляр класса DirectoryInfo и связываем его с заданным пользователем каталогом. В случае если заданный каталог не существует, выводим соответствующее сообщение на экран. Если же такой каталог существует, вызываем рекурсивный метод DelFiles (), реализующий удаление файлов.

static void DelFiles(DirectoryInfo MyDir, string FileName)

{

//вывод полного имени текущего каталога на экран

Console.WriteLine(MyDir.FullName);

//удаление всех файлов с заданным именем

FileInfo[] files = MyDir.GetFiles(FileName);

foreach (FileInfo f in files) f.Delete();

//просмотр всех вложенных в текущий каталог подкаталогов

DirectoryInfo[] SubDirs = MyDir.GetDirectories();

foreach (DirectoryInfo d in SubDirs) DelFiles(d,FileName);

}

Здесь поиск файлов, удовлетворяющих некоторому условию поиска, например по заданному расширению, осуществляется с помощью метода GetFiles (), определенного в классе DirectoryInfo. Так, например, для поиска всех текстовых файлов необходимо задать в качестве условия поиска строку “*. txt ”.

Для получения всех каталогов, вложенных в текущий, используется метод GetDirectories (). Далее для каждого из найденных подкаталогов процесс поиска и удаления файлов повторяется за счет рекурсивного вызова исходного метода.

Задания для самостоятельного выполнения:

Часть А. Каждую из этих задач решите с использованием классов StreamReader и StreamWriter. Реализуйте в консольном приложении.

1. Во входном файле input.txt в столбец записаны целые числа. В файл output.txt переписать исходные данные, а в последнюю строку – их среднее арифметическое.

2. Во входном файле input.txt в столбец записаны целые числа. В файл output.txt переписать исходные данные, а в последнюю строку – наибольшее из значений, если их несколько, то также указать количество таких элементов.

3. Во входном файле input.txt в столбец записаны целые числа. В файл out1.txt записать все четные числа, а в файл out2.txt – все нечетные.

4. Дан текстовый файл input.txt. Вставить в начало каждой строки ее номер и записать преобразованные строки в файл output.txt.

5. Во входном файле input.txt содержится произвольное количество строк, в каждой из которых записана некоторая последовательность целых чисел, разделенных пробелом. В файл output.txt вывести следующие данные о файле input.txt: количество строк и количество элементов в каждой строке.

6. Во входном файле input.txt содержится произвольное количество строк, в каждой из которых записана некоторая последовательность целых чисел, разделенных пробелом. В файл output.txt вывести максимальный элемент в каждой строке.

7. Во входном файле input.txt содержится текст, записанный строчными английскими буквами. В файл output.txt вывести этот же текст, записанный заглавными буквами.

8. Во входном файле input.txt содержится текст. В файл output.txt вывести отредактированный текст, в котором удалены лишние пробелы между словами.

9. Во входном файле input.txt содержится текст. В файл output.txt вывести в алфавитном порядке все буквы, встречающиеся в тексте и их количество.

10. Во входном файле input.txt содержится текст. В файл output.txt вывести отредактированный текст, в котором удалены все пробелы перед знаками препинания (кроме тире), а после знака препинания стоит только один пробел. Первое слово в предложении должно начинаться с заглавной буквы.

11. Во входном файле input.txt содержится текст. В файл output.txt вывести текст в зашифрованном виде: каждая буква исходного текста заменяется на следующую за ней в алфавите (буква ‘ z ’ заменяется на ‘ а ’).

Часть B. Каждую из этих задач решите тремя способами – с использованием символьного, байтового и двоичного потоков. Реализуйте в консольном приложении.

12. Создать программным образом файл input.txt и заполнить его 20 целыми числами, полученными с помощью генератора случайных чисел из диапазона [–50, 50]. Написать программу, выводящую на экран и в файл output.txt только положительные числа из файла input.txt.

13. Создать программным образом файл input.txt и заполнить его 30 целыми числами, полученными с помощью генератора случайных чисел из диапазона [–20, 40]. Написать программу, выводящую на экран и в файл output.txt максимальный и минимальный элементы файла input.txt.

14. Создать программным образом файл input.txt и заполнить его 40 целыми числами, полученными с помощью генератора случайных чисел из диапазона [0, 100]. Написать программу, выводящую на экран и в файл output.txt только те элементы файла input.txt, в записи которых есть заданная цифра (вводится с клавиатуры).

15. Создать программным образом файл input.txt и заполнить его 50 целыми числами, полученными с помощью генератора случайных чисел из диапазона [–150, 250]. Написать программу, выводящую на экран и в файл output.txt числа из файла input.txt, исключив повторные вхождения.

Часть C. Каждую из этих задач решите с использованием классов FileInfo и/или DirectoryInfo. Реализуйте в консольном приложении.

16. Написать программу, позволяющую создавать заданный пользователем каталог. Если каталог с таким именем уже существует, то вывести сообщение об этом пользователю.

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

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

19. Написать программу, которая выводит на экран список файлов MS Word, содержащихся в заданном каталоге.

20. Написать программу, которая для заданного пользователем каталога создает подкаталоги, соответствующие дате создания каждого отдельно взятого файла, и перемещает каждый файл в соответствующий дате каталог.

21. Написать программу, которая удаляет все подкаталоги, вложенные в заданный.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: