Nessuna descrizione

vivanov 8939576774 first commit 11 mesi fa
.gitignore.txt 429e5ab52f first commit 1 anno fa
readme.md 8939576774 first commit 11 mesi fa

readme.md

Конспект "Работа с потоками (stream) и файловой системой"

Работа с дисками

Класс DriveInfo имеет статический метод GetDrives, который возвращает имена всех логических дисков компьютера. Также он предоставляет ряд полезных свойств:

  • AvailableFreeSpace: указывает на объем доступного свободного места на диске в байтах
  • DriveFormat: получает имя файловой системы
  • DriveType: представляет тип диска
  • IsReady: готов ли диск (например, DVD-диск может быть не вставлен в дисковод)
  • Name: получает имя диска
  • TotalFreeSpace: получает общий объем свободного места на диске в байтах
  • TotalSize: общий размер диска в байтах
  • VolumeLabel: получает или устанавливает метку тома

Получим имена и свойства всех дисков на компьютере:

using System;
using System.IO;
 
DriveInfo[] drives = DriveInfo.GetDrives();

foreach (DriveInfo drive in drives)
{
    Console.WriteLine($"Название: {drive.Name}");
    Console.WriteLine($"Тип: {drive.DriveType}");
    if (drive.IsReady)
    {
        Console.WriteLine($"Объем диска: {drive.TotalSize}");
        Console.WriteLine($"Свободное пространство: {drive.TotalFreeSpace}");
        Console.WriteLine($"Метка: {drive.VolumeLabel}");
    }
    Console.WriteLine();
}

Вывод:

Название: C:\
Тип: Fixed                          
Объем диска: 257128853504           
Свободное пространство: 50015592448 
Метка:                              
                                    
Название: D:\                       
Тип: Fixed                          
Объем диска: 644245090304           
Свободное пространство: 211880443904
Метка: Новый том                    

Название: E:\
Тип: Fixed
Объем диска: 1356135919616
Свободное пространство: 1355963248640
Метка: Новый том

Название: F:\
Тип: Fixed
Объем диска: 742383415296
Свободное пространство: 650652270592
Метка: ssd

Работа с каталогами

Класс Directory предоставляет ряд статических методов для управления каталогами. Некоторые из этих методов:

  • CreateDirectory(path): создает каталог по указанному пути path
  • Delete(path): удаляет каталог по указанному пути path
  • Exists(path): определяет, существует ли каталог по указанному пути path. Если существует, возвращается true, если не существует, то false
  • GetDirectories(path): получает список каталогов в каталоге path
  • GetFiles(path): получает список файлов в каталоге path
  • Move(sourceDirName, destDirName): перемещает каталог
  • GetParent(path): получение родительского каталога

Класс DirectoryInfo

Данный класс предоставляет функциональность для создания, удаления, перемещения и других операций с каталогами. Во многом он похож на Directory. Некоторые из его свойств и методов:

  • Create(): создает каталог
  • CreateSubdirectory(path): создает подкаталог по указанному пути path
  • Delete(): удаляет каталог
  • Свойство Exists: определяет, существует ли каталог
  • GetDirectories(): получает список каталогов
  • GetFiles(): получает список файлов
  • MoveTo(destDirName): перемещает каталог
  • Свойство Parent: получение родительского каталога
  • Свойство Root: получение корневого каталога

Получение списка файлов и подкаталогов

string dirName = "C:\\";
 
if (Directory.Exists(dirName))
{
    Console.WriteLine("Подкаталоги:");
    string[] dirs = Directory.GetDirectories(dirName);
    foreach (string s in dirs)
    {
        Console.WriteLine(s);
    }
    Console.WriteLine();
    Console.WriteLine("Файлы:");
    string[] files = Directory.GetFiles(dirName);
    foreach (string s in files)
    {
        Console.WriteLine(s);
    }
}

Вывод:

Подкаталоги:
C:\$Recycle.Bin          
C:\$WinREAgent           
C:\AMD                   
C:\Documents and Settings
C:\MSI                   
C:\OneDriveTemp          
C:\PerfLogs              
C:\Program Files         
C:\Program Files (x86)   
C:\ProgramData           
C:\Recovery              
C:\System Volume Information
C:\tenorshare
C:\TwitchLink
C:\Users
C:\Windows

Файлы:
C:\appverifUI.dll
C:\DumpStack.log
C:\DumpStack.log.tmp
C:\hiberfil.sys
C:\pagefile.sys
C:\swapfile.sys
C:\vfcompat.dll

Обратите внимание на использование слешей в именах файлов. Либо мы используем двойной слеш: C:\, либо одинарный, но тогда перед строкой ставим знак @: @"C:\Program Files"

Можно вместо обратных слешей использовать прямые. Windows нормально их воспринимает

Создание каталога

string path = @"C:\SomeDir";
string subpath = @"program\avalon";
DirectoryInfo dirInfo = new DirectoryInfo(path);
if (!dirInfo.Exists)
{
    dirInfo.Create();
}
dirInfo.CreateSubdirectory(subpath);

Вначале проверяем, а нет ли такой директории, так как если она существует, то ее создать будет нельзя, и приложение выбросит ошибку. В итоге у нас получится следующий путь: C:\SomeDir\program\avalon

Получение информации о каталоге

string dirName = "C:\\Program Files";
 
DirectoryInfo dirInfo = new DirectoryInfo(dirName);
 
Console.WriteLine($"Название каталога: {dirInfo.Name}");
Console.WriteLine($"Полное название каталога: {dirInfo.FullName}");
Console.WriteLine($"Время создания каталога: {dirInfo.CreationTime}");
Console.WriteLine($"Корневой каталог: {dirInfo.Root}");

Удаление каталога

Нам нужно передать в метод Delete дополнительный параметр булевого типа, который укажет, что папку надо удалять со всем содержимым (иначе не получится):

string dirName = @"C:\SomeFolder";
 
try
{
    DirectoryInfo dirInfo = new DirectoryInfo(dirName);
    dirInfo.Delete(true);
    Console.WriteLine("Каталог удален");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Либо так:

string dirName = @"C:\SomeFolder";
 
Directory.Delete(dirName, true);

Перемещение каталога

string oldPath = @"C:\SomeFolder";
string newPath = @"C:\SomeDir";
DirectoryInfo dirInfo = new DirectoryInfo(oldPath);
if (dirInfo.Exists && Directory.Exists(newPath) == false)
{
    dirInfo.MoveTo(newPath);
}

При перемещении надо учитывать, что новый каталог, в который мы хотим перемесить все содержимое старого каталога, не должен существовать.

Работа с файлами. Классы File и FileInfo

Подобно паре Directory/DirectoryInfo для работы с файлами предназначена пара классов File и FileInfo. С их помощью мы можем создавать, удалять, перемещать файлы, получать их свойства и многое другое.

Некоторые полезные методы и свойства класса FileInfo:

  • CopyTo(path) копирует файл в новое место по указанному пути path
  • Create(): создает файл
  • Delete(): удаляет файл
  • MoveTo(destFileName): перемещает файл в новое место
  • Свойство Directory: получает родительский каталог в виде объекта DirectoryInfo
  • Свойство DirectoryName: получает полный путь к родительскому каталогу
  • Свойство Exists: указывает, существует ли файл
  • Свойство Length: получает размер файла
  • Свойство Extension: получает расширение файла
  • Свойство Name: получает имя файла
  • Свойство FullName: получает полное имя файла

Класс File реализует похожую функциональность с помощью статических методов:

  • Copy(): копирует файл в новое место
  • Create(): создает файл
  • Delete(): удаляет файл
  • Move: перемещает файл в новое место
  • Exists(file): определяет, существует ли файл

Получение информации о файле

string path = @"C:\apache\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
    Console.WriteLine("Имя файла: {0}", fileInf.Name);
    Console.WriteLine("Время создания: {0}", fileInf.CreationTime);
    Console.WriteLine("Размер: {0}", fileInf.Length);
}

Удаление файла

string path = @"C:\apache\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
    fileInf.Delete();
    // альтернатива с помощью класса File
    // File.Delete(path);
}

Перемещение файла

string path = @"C:\apache\hta.txt";
string newPath = @"C:\SomeDir\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
   fileInf.MoveTo(newPath);       
   // альтернатива с помощью класса File
   // File.Move(path, newPath);
}

Копирование файла

string path = @"C:\apache\hta.txt";
string newPath = @"C:\SomeDir\hta.txt";
FileInfo fileInf = new FileInfo(path);
if (fileInf.Exists)
{
   fileInf.CopyTo(newPath, true);      
   // альтернатива с помощью класса File
   // File.Copy(path, newPath, true);
}

FileStream. Чтение и запись файла

Класс FileStream представляет возможности по считыванию из файла и записи в файл. Он позволяет работать как с текстовыми файлами, так и с бинарными.

Создание FileStream

FileStream(string filename, FileMode mode)

Здесь в конструктор передается два параметра: путь к файлу и перечисление (enum) FileMode. Данное перечисление указывает на режим доступа к файлу и может принимать следующие значения:

  • Append: если файл существует, то текст добавляется в конец файл. Если файла нет, то он создается. Файл открывается только для записи.

  • Create: создается новый файл. Если такой файл уже существует, то он перезаписывается

  • CreateNew: создается новый файл. Если такой файл уже существует, то он приложение выбрасывает ошибку

  • Open: открывает файл. Если файл не существует, выбрасывается исключение

  • OpenOrCreate: если файл существует, он открывается, если нет - создается новый

  • Truncate: если файл существует, то он перезаписывается. Файл открывается только для записи.

Другой способ создания объекта FileStream представляют статические методы класса File:

FileStream File.Open(string file, FileMode mode);
FileStream File.OpenRead(string file);
FileStream File.OpenWrite(string file);

Свойства и методы FileStream

  • Свойство Length: возвращает длину потока в байтах

  • Свойство Position: возвращает текущую позицию в потоке

  • void CopyTo(Stream destination): копирует данные из текущего потока в поток destination

  • Task CopyToAsync(Stream destination): асинхронная версия метода CopyToAsync

  • int Read(byte[] array, int offset, int count): считывает данные из файла в массив байтов и возвращает количество успешно считанных байтов. Принимает три параметра:

  • array - массив байтов, куда будут помещены считываемые из файла данные

  • offset - представляет смещение в байтах в массиве array, в который считанные байты будут помещены

  • count - максимальное число байтов, предназначенных для чтения. Если в файле находится меньшее количество байтов, то все они будут считаны.

  • Task<int> ReadAsync(byte[] array, int offset, int count): асинхронная версия метода Read

  • long Seek(long offset, SeekOrigin origin): устанавливает позицию в потоке со смещением на количество байт, указанных в параметре offset.

  • void Write(byte[] array, int offset, int count): записывает в файл данные из массива байтов. Принимает три параметра:

  • offset - смещение в байтах в массиве array, откуда начинается запись байтов в поток

  • count - максимальное число байтов, предназначенных для записи

  • ValueTask WriteAsync(byte[] array, int offset, int count): асинхронная версия метода Write

Чтение и запись файлов

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

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

Посмотрим на примере считывания-записи в текстовый файл:

using System;
using System.IO;
 
namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // создаем каталог для файла
            string path = @"C:\SomeDir2";
            DirectoryInfo dirInfo = new DirectoryInfo(path);
            if (!dirInfo.Exists)
            {
                dirInfo.Create();
            }
            Console.WriteLine("Введите строку для записи в файл:");
            string text = Console.ReadLine();
 
            // запись в файл
            using (FileStream fstream = new FileStream($"{path}\note.txt", FileMode.OpenOrCreate))
            {
                // преобразуем строку в байты
                byte[] array = System.Text.Encoding.Default.GetBytes(text);
                // запись массива байтов в файл
                fstream.Write(array, 0, array.Length);
                Console.WriteLine("Текст записан в файл");
            }
 
            // чтение из файла
            using (FileStream fstream = File.OpenRead($"{path}\note.txt"))
            {
                // преобразуем строку в байты
                byte[] array = new byte[fstream.Length];
                // считываем данные
                fstream.Read(array, 0, array.Length);
                // декодируем байты в строку
                string textFromFile = System.Text.Encoding.Default.GetString(array);
                Console.WriteLine($"Текст из файла: {textFromFile}");
            }
 
            Console.ReadLine();
        }
    }
}

Изменим выше приведенную программу, применив асинхронные методы:

using System;
using System.IO;
using System.Threading.Tasks;
 
namespace HelloApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // создаем каталог для файла
            string path = @"C:\SomeDir3";
            DirectoryInfo dirInfo = new DirectoryInfo(path);
            if (!dirInfo.Exists)
            {
                dirInfo.Create();
            }
            Console.WriteLine("Введите строку для записи в файл:");
            string text = Console.ReadLine();
 
            // запись в файл
            using (FileStream fstream = new FileStream($"{path}\note.txt", FileMode.OpenOrCreate))
            {
                byte[] array = System.Text.Encoding.Default.GetBytes(text);
                // асинхронная запись массива байтов в файл
                await fstream.WriteAsync(array, 0, array.Length);
                Console.WriteLine("Текст записан в файл");
            }
 
            // чтение из файла
            using (FileStream fstream = File.OpenRead($"{path}\note.txt"))
            {
                byte[] array = new byte[fstream.Length];
                // асинхронное чтение файла
                await fstream.ReadAsync(array, 0, array.Length);
 
                string textFromFile = System.Text.Encoding.Default.GetString(array);
                Console.WriteLine($"Текст из файла: {textFromFile}");
            }
 
            Console.ReadLine();
        }
    }
}

Произвольный доступ к файлам

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

  • SeekOrigin.Begin: начало файла

  • SeekOrigin.End: конец файла

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

Курсор потока, с которого начинается чтение или запись, смещается вперед на значение offset относительно позиции, указанной в качестве второго параметра. Смещение может быть отрицательным, тогда курсор сдвигается назад, если положительное - то вперед.

Рассмотрим на примере:

using System.IO;
using System.Text;
 
class Program
{
    static void Main(string[] args)
    {
        string text = "hello world";
             
        // запись в файл
        using (FileStream fstream = new FileStream(@"D:\note.dat", FileMode.OpenOrCreate))
        {
            // преобразуем строку в байты
            byte[] input = Encoding.Default.GetBytes(text);
            // запись массива байтов в файл
            fstream.Write(input, 0, input.Length);
            Console.WriteLine("Текст записан в файл");
 
            // перемещаем указатель в конец файла, до конца файла- пять байт
            fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока
 
            // считываем четыре символов с текущей позиции
            byte[] output = new byte[4];
            fstream.Read(output, 0, output.Length);
            // декодируем байты в строку
            string textFromFile = Encoding.Default.GetString(output);
            Console.WriteLine($"Текст из файла: {textFromFile}"); // worl
 
            // заменим в файле слово world на слово house
            string replaceText = "house";
            fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока
            input = Encoding.Default.GetBytes(replaceText);
            fstream.Write(input, 0, input.Length);
 
            // считываем весь файл
            // возвращаем указатель в начало файла
            fstream.Seek(0, SeekOrigin.Begin);
            output = new byte[fstream.Length];
            fstream.Read(output, 0, output.Length);
            // декодируем байты в строку
            textFromFile = Encoding.Default.GetString(output);
            Console.WriteLine($"Текст из файла: {textFromFile}"); // hello house
        }
        Console.Read();
    }
}

Вывод:

Текст записан в файл
Текст из файла: worl
Текст из файла: hello house

Закрытие потока

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

FileStream fstream = null;
try
{
    fstream = new FileStream(@"D:\note3.dat", FileMode.OpenOrCreate);
    // операции с потоком
}
catch(Exception ex)
{
 
}
finally
{
    if (fstream != null)
        fstream.Close();
}

Если мы не используем конструкцию using, то нам надо явным образом вызвать метод Close: fstream.Close()

Чтение и запись текстовых файлов. StreamReader и StreamWriter

Класс FileStream не очень удобно применять для работы с текстовыми файлами. Для этого в пространстве System.IO определены специальные классы: StreamReader и StreamWriter.

Запись в файл и StreamWriter

Для записи в текстовый файл используется класс StreamWriter. Некоторые из его конструкторов, которые могут применяться для создания объекта StreamWriter:

  • StreamWriter(string path): через параметр path передается путь к файлу, который будет связан с потоком

  • StreamWriter(string path, bool append): параметр append указывает, надо ли добавлять в конец файла данные или же перезаписывать файл. Если равно true, то новые данные добавляются в конец файла. Если равно false, то файл перезаписываетсяя заново

  • StreamWriter(string path, bool append, System.Text.Encoding encoding): параметр encoding указывает на кодировку, которая будет применяться при записи

Свою функциональность StreamWriter реализует через следующие методы:

  • int Close(): закрывает записываемый файл и освобождает все ресурсы

  • void Flush(): записывает в файл оставшиеся в буфере данные и очищает буфер.

  • Task FlushAsync(): асинхронная версия метода Flush

  • void Write(string value): записывает в файл данные простейших типов, как int, double, char, string и т.д. Соответственно имеет ряд перегруженных версий для записи данных элементарных типов, например, Write(char value), Write(int value), Write(double value) и т.д.

  • Task WriteAsync(string value): асинхронная версия метода Write

  • void WriteLine(string value): также записывает данные, только после записи добавляет в файл символ окончания строки

  • Task WriteLineAsync(string value): асинхронная версия метода WriteLine

    using System;
    using System.IO;
     
    namespace HelloApp
    {
    class Program
    {
        static void Main(string[] args)
        {
            string writePath = @"C:\SomeDir\hta.txt";
     
            string text = "Привет мир!\nПока мир...";
            try
            {
                using (StreamWriter sw = new StreamWriter(
                    writePath, false, System.Text.Encoding.Default))
                {
                    sw.WriteLine(text);
                }
     
                using (StreamWriter sw = new StreamWriter(
                    writePath, true, System.Text.Encoding.Default))
                {
                    sw.WriteLine("Дозапись");
                    sw.Write(4.5);
                }
                Console.WriteLine("Запись выполнена");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
    }
    

Вывод:

Привет мир!
Пока мир...
Дозапись
4,5

Используем асинхронные версии методов:

using System;
using System.IO;
using System.Threading.Tasks;
 
namespace HelloApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string writePath = @"C:\SomeDir\hta2.txt";
 
            string text = "Привет мир!\nПока мир...";
            try
            {
                using (StreamWriter sw = new StreamWriter(
                    writePath, false, System.Text.Encoding.Default))
                {
                    await sw.WriteLineAsync(text);
                }
 
                using (StreamWriter sw = new StreamWriter(
                    writePath, true, System.Text.Encoding.Default))
                {
                    await sw.WriteLineAsync("Дозапись");
                    await sw.WriteAsync("4,5");
                }
                Console.WriteLine("Запись выполнена");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

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

Чтение из файла и StreamReader

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

Некоторые из конструкторов класса StreamReader:

  • StreamReader(string path): через параметр path передается путь к считываемому файлу

  • StreamReader(string path, System.Text.Encoding encoding): параметр encoding задает кодировку для чтения файла

Среди методов StreamReader можно выделить следующие:

  • void Close(): закрывает считываемый файл и освобождает все ресурсы

  • int Peek(): возвращает следующий доступный символ, если символов больше нет, то возвращает -1

  • int Read(): считывает и возвращает следующий символ в численном представлении. Имеет перегруженную версию: Read(char[] array, int index, int count), где array - массив, куда считываются символы, index - индекс в массиве array, начиная с которого записываются считываемые символы, и count - максимальное количество считываемых символов

  • Task<int> ReadAsync(): асинхронная версия метода Rea

  • string ReadLine(): считывает одну строку в файле

  • string ReadLineAsync(): асинхронная версия метода ReadLine

  • string ReadToEnd(): считывает весь текст из файла

  • string ReadToEndAsync(): асинхронная версия метода ReadToEnd

    using System;
    using System.IO;
    using System.Threading.Tasks;
     
    string path = @"C:\SomeDir\hta.txt";
    
    try
    {
    using (StreamReader sr = new StreamReader(path))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
    // асинхронное чтение
    using (StreamReader sr = new StreamReader(path))
    {
        Console.WriteLine(await sr.ReadToEndAsync());
    }
    }
    catch (Exception e)
    {
    Console.WriteLine(e.Message);
    }
    

Считаем текст из файла построчно:

string path= @"C:\SomeDir\hta.txt";
   
using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}
// асинхронное чтение
using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))
{
    string line;
    while ((line = await sr.ReadLineAsync()) != null)
    {
        Console.WriteLine(line);
    }
}