Лабораторная работа №6. Файлы.

Класс FileStream

Класс FileStream представляет возможности по считыванию из файла и записи в файл. Он позволяет работать как с текстовыми файлами, так и с бинарными. Он создаётся следующим образом:
FileStream fileStream = new FileStream(path, FileMode.Create);
Здесь в конструктор передается два параметра: путь к файлу и перечисление FileMode. Данное перечисление указывает на режим доступа к файлу и может принимать следующие значения: 

  • Append: если файл существует, то текст добавляется в конец файл. Если файла нет, то он создается. Файл открывается только для записи.
  • Create: создается новый файл. Если такой файл уже существует, то он перезаписывается
  • CreateNew: создается новый файл. Если такой файл уже существует, то он приложение выбрасывает ошибку
  • Open: открывает файл. Если файл не существует, выбрасывается исключение
  • OpenOrCreate: если файл существует, он открывается, если нет - создается новый
  • Truncate: если файл существует, то он перезаписывается. Файл открывается только для записи.

Также можно использовать статические методы класса File, например

FileStream fileStream = File.Create(path); // создать новый файл
или
FileStream fileStream = File.Open(path); // открыть файл с доступом на чтение и запись

Для работы с бинарными файлами предназначена пара классов BinaryWriter и BinaryReader. Эти классы позволяют читать и записывать данные в двоичном формате.

Основные метода класса BinaryWriter

  • Close(): закрывает поток и освобождает ресурсы
  • Flush(): очищает буфер, дописывая из него оставшиеся данные в файл
  • Seek(): устанавливает позицию в потоке
  • Write(): записывает данные в поток

Объект BinaryWriter создаётся так:

BinaryWriter binaryWriter = new BinaryWriter(fileStream);
Для того, чтобы закрыть потоки и освободить ресурсы можно использовать метод Close(). Но удобнее использовать директиву using
using (FileStream fileStream = new FileStream(path, FileMode.Create))
using (BinaryWriter binaryWriter = new BinaryWriter(fileStream))
{
    // запись информации в файл
}
или можно сократить запись:
using (var binaryWriter = new BinaryWriter(File.Create(path)))
{
    // запись информации в файл
}

Бинарные файлы (запись)

  1. Создайте метод
    static void CreateTheNewFile(string path, params int[] values)
    
    который записывает в файл целых чисел набор значений, перечисленных через запятую.
    Указание Есть разные виды ошибок, при котором работа с файлом невозможна, например отсутствие прав доступа к файлу. Чтобы программа не "вылетала", используйте конструкцию try...catch
    try
    {
        // код, вызывающий ошибку
    }
    catch (IOException e)
    {
        Console.WriteLine($"Ошибка обработки файла {e.Message}");
    }
    
    Используйте try... catch в методе или поместите в блок try вызов метода.


Чтение элементов из файла (последовательный доступ)

Для чтения значений из файла используем объект типа BinaryReader и его методы ReadBoolean(), ReadChar(), ReadDouble(), ReadInt32() и т.д. Создание объекта BinaryReader аналогично созданию объекта класса BinaryWriter. Для последовательного чтения элементов из файла используем цикл:
using (FileStream fileStream = File.OpenRead(path))
using (BinaryReader binaryReader = new BinaryReader(fileStream))
{
      while (binaryReader.PeekChar()!=-1)
      {
            // обработка значений        
       }
}
  1. Создайте метод
    static void ReadFile(string path, uint n = 10, string delimiter = ", ")
    
    который принимает имя файла целых чисел и выводит их на консоль по N чисел в каждой строке (значение по умолчанию для N = 10, N > 0). Для пустого файла выводить сообщение <empty file>. (Используем свойство Length объекта FileStream)

Чтение элементов из файла (произвольный доступ)

В четырёх следующих задачах используем Метод long Seek(long offset, SeekOrigin origin) устанавливает позицию в потоке со смещением на количество байт, указанных в параметре offset. SeekOrigin - перечисление с тремя значениями 

  • SeekOrigin.Begin: начало файла
  • SeekOrigin.End: конец файла
  • SeekOrigin.Current: текущая позиция в файле

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

using (FileStream fileStream = File.OpenRead(path))
using (BinaryReader binaryReader = new BinaryReader(fileStream))
{
   fileStream.Seek(3 * sizeof(int), SeekOrigin.Begin);
   Console.Write(binaryReader.ReadInt32());
}
  1. Дан бинарный файл целых чисел. Обнулить его минимальный элемент (считать, что в файле он единственный). Если файл пуст, ничего не делать.
    Указание. Во-первых, следует найти позицию минимального элемента (пользуясь Position).
    Во вторых, с помощью метода Seek, следует перейти в позицию минимального элемента и записать в файл переменную с нулевым значением.
    Тестирование. В основной программе явно создайте и обработайте четыре файла (обязательно проверить пустой и файл из одного элемента). Для каждого файла: распечатать исходное содержимое, обнулить минимальный элемент, распечатать содержимое файла после изменения. 
    Указание к тестированию. Удобно записать имена созданных файлов в массив строк и обработать их в цикле.
  2. Дан бинарный файл целых чисел. Увеличить все его элементы в два раза.
    Тестирование. В основной программе явно создайте и обработайте четыре файла (обязательно проверить пустой и файл из одного элемента).
  3. Дан файл целых чисел (возможно пустой). Инвертировать его (то есть изменить в нём порядок элементов на обратный).
    Замечание. Не забудьте, что использовать вспомогательный файл запрещается.
    Тестирование. В основной программе создать и обработать четыре файла (обязательно проверить пустой, файл из одного и двух элементов). 
  4. Дан бинарный файл. Удвоить его размер, записав в конец файла все его исходные элементы в обратном порядке.
    Замечание.
     Не забудьте, что использовать вспомогательный файл запрещается.
    Тестирование. В основной программе явно создайте и обработайте три файла (обязательно пустой файл, файл из одного целого числа).

Обработка текстовых файлов

// Цикл по строкам файла - первый способ
using (var sr = new StreamReader("a.txt"))
{
    string line;
    while ((line = sr.ReadLine()) != null)
        Console.WriteLine(line);
}
//Цикл по строкам файла - второй способ
using (var sr = new StreamReader("a.txt"))
{
    while (!sr.EndOfStream)
        Console.WriteLine(sr.ReadLine());
}
  1. Дан текстовый файл. Вывести все символы, содержащиеся в этом файле, и их коды (используйте метод Read()) . Посчитать общее количество символов в выходную переменную метода. Выяснить, какие коды у символов, обозначающих конец строки (ответ укажите в виде комментария). Выяснить код пробела.
  2. Дано целое неотрицательное число N и символ C. Создать текстовый файл, содержащий строк, первая из которых состоит из одного символа C, вторая — из двух, третья — из трёх и т.д. (Пользуемся методами Write() и WriteLine()) 
  3. Даны два текстовых файла. Дописать содержимое второго файла в конец первого. (Используем StreamWriter, найцдите подходящий конструктор)
  4. Дан текстовый файл. Удалить из него все пустые строки.
    Указание. Все задачи на изменение содержимого текстового файла (кроме дополнения) решаются с использованием временного файла. Создание временного файла обсуждается на данной странице. Вам необходимо написать вспомогательную функцию MakeTempFileName для генерации имени временного файла. Переделайте функцию с данной страницы на язык C# и инициализируйте newFileName по описанному на странице принципу.

Средства System.IO.File для работы с текстовыми файлами

  1. Дан текстовый файл. Определить количество пустых строк в этом файле, используя статический метод ReadLines класса System.IO.File.
  2. Дан csv-файл, содержащий целые числа. Найти сумму чисел в каждой строке файла (решение оформить в виде функции, возвращающей массив целых чисел). Для пустой строки следует возвращать ноль.
    Замечание 1. Каждая строка файла в формате CSV (comma-separated values — значения, разделённые запятыми) содержит значения, разделённые запятыми.
    Указание 1. Использовать метод Split для извлечения чисел из строк и метод int.Parse для преобразования их к типу integer для суммирования.
    Замечание 2. Задачу можно решать с использованием явных циклов, либо с использованием методов последовательностей.
  3. Данная задача не требует чтения содержимого файлов.
    • Создать в текущем каталоге папку text_files, создать там несколько текстовых файлов просто «руками», можете скопировать туда имеющиеся файлы).
    • Вывести имена файлов каталога text_files (процедура с одним параметром — именем каталога).
    • Переименовать все текстовые файлы каталога text_files, добавив к именам префикс help-. Это следует выполнить в процедуру с двумя параметрами — именем каталога и префиксом).
    • Снова вывести имена файлов каталога.

    Указания.

    1. Чтобы получить список имён файлов каталога, используйте статический метод System.IO.Directory.GetFiles. Текущему каталогу соответствует имя '.', родительскому — '..'.
    2. Чтобы отделить собственно имя файла от всего пути к файлу (абсолютного или относительного) используйте статический метод System.IO.Path.GetFileName.
    3. Для формирования имени файла с путём по пути и имени используйте System.IO.Path.Combine.

    4. Чтобы переименовать файл, используйте статический метод System.IO.File.Move.

  4. Дано натуральное число K и текстовый файл, содержащий слова. Создать текстовый файл и записать в него все слова длины K из исходного файла по одному слову в строке.
    Указание. Воспользуйтесь методом System.IO.File.ReadAllText для чтения исходного файла и System.IO.File.WriteAllLines для записи нового файла.
    Замечание. Словом считать набор символов, не содержащий пробелов, знаков препинания и ограниченный пробелами, знаками препинания или началом/концом строки. Если исходный файл не содержит слов длины K, то оставить результирующий файл пустым.
    Тестирование. Создать каталог input-files/task-04 и добавить туда несколько входных текстовых файлов, на которых вы проводите тестирование. Убедиться, что соответствующие результирующие файлы содержат то, что нужно.
  5. Дан текстовый файл, в каждой строке которого записано одно или несколько слов. Создать новый текстовый файл из одной строки, в которой через пробел записаны первые слова строк исходного файла, заканчивающиеся на заданную подстроку.