С# для Windows Более подробно о потоках и работе с файлами

К началу раздела

Более подробно о потоках и работе с файлами

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


В начало

Потоковые классы в C#

В пространстве имен System.IO потоки представлены:

  • абстрактным классом Stream и его наследниками, обеспечивающими возможности манипулирования последовательностями байтов;

  • прямыми наследниками класса Object - классами BinaryReader и BinaryWriter, предназначенными для чтения и записи примитивных типов данных;

  • наследниками абстрактного класса TextReader - StreamWriter и StreamReader, использующимися для чтения и записи последовательности символов;

  • наследниками абстрактного класса TextReader - StringWriter и StringReader, использующимися для чтения и записи строк;

В библиотеке классов .NET Framework Class Library существует несколько дополнительных классов в других пространствах имен и базирующихся на абстрактном классе TextWriter, предназначенных для манипуляции файлами в формате HTML документов - о них речь будет вестись в другом разделе. В этом разделе рассматривается все, что связано с потоками и записью в файлы и чтением из файлов примитивных типов данных в их различном представлении.


В начало

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

В предыдущем параграфе при рассмотрении пространства имен IO было отмечено, что класс Stream - наследник абстрактного класса MarhalByRefObject - также абстрактный класс, обеспечивающий представление последовательности байтов. Классы наследники класса Stream, обеспечивают функции для работы с файлами (класс FileStream), памятью (класс MemoryStream) и дают возможность создать буфер в памяти для буферизованных потоковых операций чтения и записи (BufferedStream). Потоки поддерживают операции чтения, записи, а также операции позиционирования. Из наследников абстрактного класса Stream следует упомянуть о еще двух классах из другого пространства имен - классах NetworkStream (класс для посылки данных по сети) и CryptoStream (класс для криптографических преобразований), которые имеют схожие и отличные свойства и методы от методов потоков пространства имен IO, что пока также выходит за границу задач данного раздела.

Базовый класс Stream

Класс Stream содержит несколько базовых методов и свойств для всех потоков наследников - это:

  • Методы используемые для начала и завершения асинхронных операции чтения и записи:

    - Метод BeginRead(byte[] buffer,int offset,int count,AsyncCallback callback,object state);

    Начать асинхронную операцию чтения.

    Параметры: buffer - буфер данных, offset - смещение байта в потоке, count - максимальное число байтов для чтения, callback - параметр типа AsyncCallback или проще - функция делегат, которая вызывается при завершении операций чтения записи, state - параметр типа AsyncState доступный через свойства AsyncState интерфейса IAsyncResult и возвращающий метод IAsyncResult. Тоесть это объект(класс), который может быть создан и заполнен пользователем некоторой информацией, которая в той или иной степени отражает состояние этапа вызова асинхронной операции, и может быть использована в дальнейшем или при прерывании или в функции обратного вызова, имя объекта доступно в функции callback.

    Возвращаемое значение - IAsyncResult - интерфейс при помощи которого программа может получить информацию о состоянии асинхронной операции.

    Метод вызывает прерывания IOException при выходе за границы файла (потока) и сбоях на дисках, ArgumentException - при недопустимых параметрах, ObjectDisposedException Methods - при вызовах методов для закрытого потока, NotSupportedException - если поток не поддерживает метод.

    Подробный пример по работе с асинхронными методами будет рассмотрен после рассмотрения метода EndRead() (см. ниже).

    - Метод EndRead(IAsyncResult asyncResult); завершает асинхронное чтение.

    Возвращаемое значение число прочитанных байт.

    Метод вызывает прерывания ArgumentNullException asyncResult - при нулевой ссылки параметра, ArgumentException - когдат объект IASYNCRESULT не был создан вызовом BeginRead и InvalidOperationException - при множественном вызове.

    Пример работы с асинхронными методами

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

    private void button1_Click(object sender, System.EventArgs e)
    {
     //Создаем процесс на базе функции AsynchronRead
     Thread thread1 = new Thread(new ThreadStart(AsynchronRead));
     thread1.Start();
    }
    

    В функции AsynchronRead собственно и будет вызов асинхронного метода чтения из файла:

    private void AsynchronRead()
    {
     //Файл создан заранее его объем должен быть достаточно большим
     string path = "C:\\a.txt";
     //Создаем функцию  callback EndAsingRead задача которой 
     //будет оповестить основной процесс о завершении асинхронной
     //операции
     AsyncCallback asynccallback = new AsyncCallback(EndAsingRead);
     //Создаем объект state - экземпляр структуры Situation, 
     //где будем хранить все наши параметры и, в том числе, 
     //и прочитанные данные
     Situation situation = new Situation();
     //Создаем поток для чтения
     situation.filestream = new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.None,
    	16,        //размер буфера
    	true);     //Допускается асинхронный доступ
     //В структуре Situation должны поместиться все байты файла
     situation.bufSize = situation.filestream.Length;
     situation.bText = new byte [situation.bufSize];		
     //Создаем объект сигнализатор
     situation.manualresetevent = new ManualResetEvent(false);
     //Стартуем операцию чтения в буфер bText
     situation.filestream.BeginRead(situation.bText,0,  //Куда читать и с какого места
    		(int)situation.filestream.Length,   //Читать будем весь файл
    		asynccallback,                      //Вызывается по окончании чтения
                                                        //в качестве параметра ей передается
                                                        //возвращаемое значение функции
    		situation);                         //Информационный объект	
      //Приостанавливаем процесс до завершения чтения
      //пока не будет выполнено manualresetevent.Set()
      //Оноже будет выполнено только по окончанию
      //операции чтения			
      situation.manualresetevent.WaitOne();
      //Выведем данные по окончанию чтения - тоесть
      //после situation.manualresetevent.Set;
      textBox1.Text=System.Text.Encoding.Default.GetString(situation.bText);
    }
    

    Структурра и функция обратного вызова используемые при вызове асинхронного метода:

    struct Situation 
    {
     //Наш поток
     public FileStream filestream;
     //Здесь будем хранить данные
     public byte [] bText;
     //Размер буфера
     public long bufSize;
     //Объект типа ManualResetEvent обычно используется  для 
     //сигнализации о каком-то событии сразу нескольким нитям прооцессов 
     public ManualResetEvent manualresetevent;
    }
    static void EndAsingRead(IAsyncResult iasyncresult)
    {
     //имя объекта Situation доступно в функции callbak  
     Situation  situation = (Situation)iasyncresult.AsyncState;	
     //Можно при завершении синхронного чтения узнать 
     //число прочитанных байт
     int readCount = situation.filestream.EndRead(iasyncresult);
    
     situation.filestream.Close();
     //Снимаем тормоз
     situation.manualresetevent.Set(); 
     situation.manualresetevent.Close();
    }
    

    Код примера выводит содержимое текстового файла только после окончания операции чтения. То, что это так, легко убедиться закоментировав строку situation.manualresetevent.Set(); в функции EndAsingRead - в этом случае данные не будут выведены вообще.

    - Метод BeginWrete(byte[] buffer,int offset,int count,AsyncCallback callback,object state);

    Начать асинхронную операцию записи.

    Параметры: buffer - буфер данных, offset - смещение байта в потоке, count - максимальное число байтов для чтения, callback - параметр типа AsyncCallback или проще - функция делегат, которая вызывается при завершении операций чтения записи, state - параметр типа AsyncState доступный через свойства AsyncState интерфейса IAsyncResult и возвращающий метод IAsyncResult. Тоесть это объект(класс), который может быть создан и заполнен пользователем некоторой информацией, которая в той или иной степени отражает состояние этапа вызова асинхронной операции, и может быть использована в дальнейшем или при прерывании или в функции обратного вызова, имя объекта доступно в функции callback.

    Возвращаемое значение - IAsyncResult - интерфейс при помощи которого программа может получить информацию о состоянии асинхронной операции.

    Метод вызывает прерывания IOException при ошибках ввода/вывода, ArgumentException - при недопустимых параметрах когда смещение или индекс находятся в недопустимых пределах, ObjectDisposedException Methods - при вызовах методов для закрытого потока, NotSupportedException - если поток не поддерживает метод, ArgumentOutOfRangeException - смещение или индекс отрицательны, ArgumentNullException при нулевой ссылке на массив.

    Как видно параметры и прерывания для BeginWrete полностью аналогичны параметрам и прерываниям метода BeginRead, как аналогичен и алгоритм использования метода в приведенном выше примере, в котором достаточно поменять BeginRead на BeginWrete и заранее заполнить источник situation.bText требуемой информацией, а в функции обратного вызова использовать метод EndWrite вместо EndRead. Всилу этого, пример для данного метода не приводится - каждый может легко использовать (приведенный выше пример).

    - Метод EndWrete(IAsyncResult asyncResult); завершает асинхронную запись.

    Возвращаемого значения нет.

    Метод вызывает прерывания ArgumentNullException asyncResult - при нулевой ссылки параметра, ArgumentException - когдат объект IASYNCRESULT не был создан вызовом BeginRead и InvalidOperationException - при множественном вызове. О использовании метода мы уже говорили при описании метода BeginWrete.

  • Синхронные методы чтения записи:

    - Метод ReadByte() - читает один байт из файла и продвигает указатель на один байт.

    Возвращаемое значение - прочитанный байт.

    Запишем в файл C:\a.txt числа 0123456789 и выполним следующий код:

    private void button1_Click(object sender,System.EventArgs e)
    {
     string path = "C:\\a.txt";
     using(FileStream  
          fileStream = new FileStream(path, FileMode.Open))
     {
      fileStream.Seek(1, SeekOrigin.Begin);
      //Читаем и выводим данные
      for(int i = 0; i < fileStream.Length;i++)
      {
       //Используем метод ReadByte()
       textBox1.Text+=fileStream.ReadByte()+" ";
      }
    }
    

    Результат в TextBox

    495051525354555657-1
    

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

    - Метод WriteByte(byte value) - записывает байт с текущей позиции в потоке и продвигает указатель на один байт.

    Возвращаемого значения нет.

    Метод вызывает прерывания ObjectDisposedException Methods - при вызовах методов для закрытого потока, NotSupportedException - если поток не поддерживает метод.

    private void button1_Click(object sender, System.EventArgs e)
    {  
     string path = "C:\\a.txt";
     //Будем использовать FileInfo	
     FileInfo fileinfo=new FileInfo(path); 
     using(FileStream  
         fileStream = fileinfo.OpenWrite())
     {
      //При создании потока указатель и так на нуле
      fileStream.Seek(0, SeekOrigin.Begin);        
      //Массив из 10 байт
      byte[] bArray=new byte[10];
      Random numrandom = new Random();
      for(int i=0; i < bArray.Length; i++)
      {
       //Записываем случайные числа в массив
       bArray[i]=(byte)numrandom.Next(48,57);
       fileStream.WriteByte(bArray[i]);
       textBox1.Text+= bArray[i];
      }				
     }
    }
    

    Результат:

    //В файле
    7664734068
    //В контроле
    55545452555152485456
    

    - Метод Read(bute[] buffer,int offset,int count) - читает блок размером count байт из потока с позиции байта offset и пишет данные в буфер buffer или массив байт.

    Возвращаемое значение - общее число прочитанных байт или "0" - если достигнут конец потока. Если операция чтения успешна, текущая позиция потока продвигается на count байт.

    Метод вызывает прерывания IOException при ошибках ввода/вывода, ArgumentException - при недопустимых параметрах когда смещение или индекс находятся в недопустимых пределах, ObjectDisposedException Methods - при вызовах методов для закрытого потока, NotSupportedException - если поток не поддерживает метод, ArgumentOutOfRangeException - смещение или индекс отрицательны, ArgumentNullException при нулевой ссылке на массив.

    Пример использования метода:

    private void button1_Click(object sender, System.EventArgs e)
    {  
     string path = "C:\\a.txt";
     FileInfo fileinfo=new FileInfo(path); 
     using(FileStream  
     fileStream = fileinfo.OpenRead())
     {
      //При создании потока указатель и так на нуле
      fileStream.Seek(0, SeekOrigin.Begin);
      byte[] bArray=new byte[10];
      //Читаем 10 байт
      int iBytes=fileStream.Read(bArray,0,10);
      //Проверяем, что все верно прочитано
      if(iBytes == 10)
      {
       for(int i=0; i < iBytes; i++)
       textBox1.Text+= bArray[i];
      }
    }						
    

    Результат:

    48495051525354555657
    

    - Метод Write(bute[] buffer,int offset,int count) - пишет блок размером count байт в поток с позиции байта offset из буфера байт buffer.

    Возвращаемое значение - нет.

    Метод вызывает прерывания IOException при ошибках ввода/вывода, ArgumentException - при недопустимых параметрах когда смещение или индекс находятся в недопустимых пределах, ObjectDisposedException Methods - при вызовах методов для закрытого потока, NotSupportedException - если поток не поддерживает метод, ArgumentOutOfRangeException - смещение или индекс отрицательны, ArgumentNullException при нулевой ссылке на массив байт.

    Пример использования метода (изменим код примера для метода WriteByte и получим тотже результат):

    for(int i=0; i < bArray.Length; i++)
    {
     //Записываем случайные числа в массив
     bArray[i]=(byte)numrandom.Next(48,57);					
     textBox1.Text+= bArray[i];
    }				
    //Записываем сразу 10 байт
    fileStream.Write(bArray,0,10);
    
  • Метод позиционирования

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

    Метод может вызвать прерывания IOException - если указатель файла перемещен в недопустимое местоположение или ArgumentException - значение SeekOrigin недопустимо для данного местоположения указателя.

    Параметры: offset - cмещение от начала, конца или текущей позиции, origin - значение типа SeekOrigin, определяющее начало, конец, или текущую позицию.

    Возможные прерывания как и у методов чтения записи: NotSupportedException - поток не поддерживает позиционирование, IOException - при ошибках ввода вывода, а также ArgumentException - при установке указателя вне размера потока и ObjectDisposedException - метод вызван для закрытого потока.

  • Метод изменения длины потока

    - SetLength(long value) - метод изменяет длину текущего потока и возвращает новую позицию указателя в потоке;

    Параметр value - новая длина потока.

    Возможные прерывани: NotSupportedException - поток не поддерживает установку размеров, IOException - при ошибках ввода вывода, а также ArgumentOutOfRangeException - при попытке установить параметр длины меньше нуля.

  • Метод очистки буфера

    - МетодFlush() - очищает все буфера для данного потока и заставляет записать в поток все буферизованные данные;

    Метод вызывает прерывание IOException при ошибке ввода/вывода и ObjectDisposedException - при применении метода к закрытому потоку.

  • Метод закрытия потока

    - МетодClose() - метод закрывает текущий поток и освобождает все ресурсы, связанные с текущим потоком и файлом.

    Метод вызывает прерывание IOException если ошибка произошла при попытке закрыть поток.

  • Свойство определяющие длину потока:

  • long Length - длина потока в байтах.

  • Свойства определяющие возможность выполнения той или иной операции:

    - CanRead, CanSeek, CanWrite

    Если свойство равно false, то нельзя использовать соответствующие свойства и методы, показанные в таблице:

    Свойства 

    Закрытые методы и свойства

    CanRead = false   

    Read, ReadByte, BeginRead, EndRead, Peek

    CanSeek = false   

    Length, SetLength, Position, Seek

    CanWrite = false   

    SetLength, Write, BeginWrite, EndWrite, WriteByte



В начало

Классы FileStream и BufferedStream работа с файлами


В начало

Класс FileStream

Как мы отмечали выше, класс Stream служит в качестве базового класса для нескольких типов потоков и класса FileStream в частности.

В общем случае поток FileStream мы уже широко использовали при работе с файлами - все примеры при рассмотрении методов абстрактного класса Stream были построены с его применением.

Класс FileStream определен как

public FileStream(string path,FileMode mode,FileAccess access,
                  FileShare share,int bufferSize,bool useAsync);

В общем случае класс имеет 9 конструкторов, различающихся числом передаваемых в конструктор параметров - все параметры кроме первого могут быть использованы по умолчанию.

При создании потока типа FileStream указываются:

  • path - имя файла для которого создается поток;

  • mode - способ открытия (FileMode.Open - открыть имеющийся файл, FileMode.Create - создать новый, FileMode.OpenOrCreate - открыть имеющийся или создать новый файл);

  • access - тип доступа (FileAccess.Read - только чтение, FileAccess.Write - только запись, FileAccess.ReadWrite - чтение и запись);

  • share - ограничения при разделяемом доступе (FileShare.None - недопустимо совместное использование, FileShare.Read - допускается совместное открытие потоков для одного файла для чтения, FileShare.ReadWrite - допускается совместное открытие потоков для одного файла для чтения и записи и FileShare.Write - допускается совместное открывать потоки на запись для одного файла - по умолчанию FileShare.Read);

  • bufferSize - желательный размер буфера в байтах. Минимальный размер буфера даже при задании значения от 0 до 8 установливается в восемь байтов.

  • useAsync - указывает допустимость в потоке использования асинхронных методов доступа.

Пример создания потока (из примера приведенного выше):

filestream = new FileStream
       (path,FileMode.Open,
        FileAccess.Read,
        FileShare.None,
	16,        //размер буфера в байт
	true);     //Допускается асинхронный доступ

При невозможности создания потока возбуждается исключение одного из возможных типов:

  • ArgumentNullException - path есть нулевая ссылка;

  • ArgumentException - path пустая строка (" ") или содержит только незаполненное пространство, или содержит один или более недопустимых символов.

  • ArgumentOutOfRangeException - bufferSize отрицательно или имеет нулевое значение или режим, доступ, или ресурс содержат недопустимое значение.

  • FileNotFoundException - Файл не найден для данного типа FileMode;

  • IOException - ошибка ввода/вывода или тип FileMode - CreateNew и файл, указанный путем уже существует.

  • SecurityException - процесс не имеет требуемого разрешения на доступ к файлу;

  • DirectoryNotFoundException - указанный путь недопустим;

  • UnauthorizedAccessException - доступ не разрешен операционной системой для указанного пути, например, когда доступ ReadWrite, а файл или каталог только для чтения.

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

Класс FileStream можно использовать (как это было видно из приведенных выше примеров) для чтения и записи байтов, а также символов, строк и других типов данных.

Класс FileStream поддерживает синхронное и асинхронное открытие и синхронные и асинхронные операции чтения и записи байт и блоков данных (используются методы класса Stream). Режим по умолчанию - синхронный. Для проверки режима открытия потока используется свойство IsAsync. Поток поддерживает произвольный доступ (метод Seek). Текущую позицию указатель можно определить через свойство Position. Методы Lock и Unlock могут быть использованы для предотвращения доступа ко всему файлу или к его части (Look) или отмены ранее установленного запрета доступа(UnLook).

Свойство Length возвращает длину потока в байтах, а метод SetLength позволяет установить длину потока в байтах.

Наследник класса FileStream класс IsolatedStorageFileSystem (пространство имен System.IO.IsolatedStorage) дополняет класс FileStream методами создания, чтения и записи файлов в изолированном хранилище - тоесть файлы в виртуальной файловой системе, в которой данные недоступны извне (изоляция данных на уровне пользователя - сборки или домена приложения).

Большинство методов и свойств мы уже использовали, остановимся лиш на двух методах, которые мы еще не касались - это Look и Unlook:

  • public virtual void Lock(long position,long length); - метод используется для предотвращения доступа ко всему файлу или к его части.

    Параметры:

    • position - байт потока с которого осуществляется блокировка (>= 0);

    • length - диапазон, который будет блокирован.

    Возможные прерывания:

    • ArgumentOutOfRangeException - position или length отрицательны.

    • ObjectDisposedException - файл закрыт.

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

    Создадим два процесса и объект ManualResetEvent для того, чтобы можно было "затормозить" какой либо процесс при блокировки файла. Для этого вначале запишим объявление объекта:

    public class Form1 : System.Windows.Forms.Form
    {
     private ManualResetEvent manualresetevent;
    

    А в обработчики нажатия кнопки 1 запишим инициализацию бъекта и процессов, выполнение которых сводится к выполнению кода функций AsynchronProcess1 и AsynchronProcess2.

    private void button1_Click(object sender, System.EventArgs e)
    {
     manualresetevent=new ManualResetEvent(false);
     Thread thread1 = new Thread(new ThreadStart(AsynchronProcess1));
     //Процесс 1 стартует первым
     thread1.Start();
     Thread thread2 = new Thread(new ThreadStart(AsynchronProcess2));
     thread2.Start();
    

    Функция AsynchronProcess1 показывает, что из процесса первого обратившегося к файлу делать можно все, даже при установленной блокировки. Перед окончанием процесса он преостанавливается, но блокировка для файла остается до практически выполнения большей части процесса 2.

    private void AsynchronProcess1()
    {
     string path = @"C:\a.txt";
     //Будем пользоваться кодировкой Unicode - ее мы еще не использовали
     UnicodeEncoding unicodeencoding = new UnicodeEncoding();
     //Текстовые строки для записи в файл
     string sText1 = "Эта первая строка из первого процесса, записанная в файл!";
     string sText2 = "Эта вторая строка из первого процесса, записанная в файл!";
     //Длины текстов в байтах
     int sTextLength1 = unicodeencoding.GetByteCount(sText1);			
     int sTextLength2 = unicodeencoding.GetByteCount(sText2);			
     //Массивы для чтения байт из потока
     byte[] bText1 = new byte[sTextLength1];
     byte[] bText2 = new byte[sTextLength2];
     //Создаем поток для записи и чтения в файл
     using(FileStream filestream = new FileStream(path, FileMode.OpenOrCreate, 
    			  FileAccess.ReadWrite, FileShare.ReadWrite))
     {
      //Процесс стартовал первым - здесь можно все
      //Записываем первый текст в файл
      filestream.Write(unicodeencoding.GetBytes(sText1),0, sTextLength1);					
      //Переносим данные в файл
      filestream.Flush();
      //Уходим на начало
      filestream.Seek(0,SeekOrigin.Begin);
      //Читаем текст				
      filestream.Read(bText1,0, sTextLength1);								
      textBox1.Text+="1. Чтение из первого процесса - блокировки нет\r\n     "
    			+unicodeencoding.GetString(bText1)+"\r\n";
      //Блокируем 4 байта начиная с 6го от 0
      filestream.Lock(6,4);
      //Вновь на начало
      //Обработка исключения поставлена для того, чтобы показать,
      //что исключений в данном случае не возникает
      try
      {
       filestream.Seek(0,SeekOrigin.Begin);				
       //Очистить можно и так
       bText1= new byte[sTextLength1];
       //Читаем весь текст из файла - хотя уже стоит блокировка
       filestream.Read(bText1,0, sTextLength1);								
       textBox1.Text+="2. Чтение из первого процесса - блокировка есть\r\n    "
    	+unicodeencoding.GetString(bText1)+"\r\n";					
       //Сдвигаемся на 8 ой символ
       filestream.Seek(0,SeekOrigin.Begin);
       //Меняем символ
       filestream.Write(unicodeencoding.GetBytes(sText2),0, sTextLength2);	
       filestream.Flush();
       textBox1.Text+=
        "Записать из первого процесса можно в заблокированный файл - прерывания нет\r\n";
       //Вновь на начало
       filestream.Seek(0,SeekOrigin.Begin);
       //Читаем измененный текст
       bText2= new byte[sTextLength2];
       filestream.Read(bText2,0,sTextLength2);
       textBox1.Text+="3. Чтение из первого процесса измененного - блокировка есть\r\n    "
    	+unicodeencoding.GetString(bText2)+"\r\n";		
      }
      catch
      {
       textBox1.Text+="Ошибка при работе с блокированным файлом\r\n";
      }
      //Тормозим процесс - а значит файл останется заблокированным
      manualresetevent.WaitOne();				
      //После того как второй процесс снимет блокировку
      filestream.Unlock(6,4);
      textBox1.Text+="\r\nБлокировка файла снята \r\n";
    	textBox1.Text+="Процесс 1 завершен \r\n";
    }
    

    Второй процесс стартует после первого и для него уже файл частично блокирован:

    						
    private void AsynchronProcess2()
    {
     string path = @"C:\a.txt";			
     UnicodeEncoding unicodeencoding = new UnicodeEncoding();
     //Текстовая строка для записи в файл
     string sText = "Строка из второго процесса записанная в файл!";
     //Длина текста в символах Unicode
     int sTextLength = unicodeencoding.GetByteCount(sText);			
     //Массив для чтения байт из потока
     byte[] bText = new byte[sTextLength];
     //Создаем поток для записи и чтения в файл
     using(FileStream filestream1 = new FileStream(path, FileMode.OpenOrCreate, 
    		  FileAccess.ReadWrite, FileShare.ReadWrite))
     {
      //Здесь прерывания не будет
      try
      {
       filestream1.Seek(sTextLength,SeekOrigin.Begin);
       filestream1.Seek(0,SeekOrigin.Begin);
       textBox1.Text+="Указатель сдвинуть можно хотя файл и заблокирован \r\n";
      }
      catch
      {
       textBox1.Text+="Указатель сдвинуть нельзя   - файл заблокирован \r\n";
      }
      //Читаем текст и хотя читаем всего два байта, но чтение блоками
      //и будет прерывание, так как шестой байт в первои блоке на носителе
      try
      {
       filestream1.Read(bText,0,2);												
       textBox1.Text+="4. Чтение из второго процесса - блокировка есть\r\n    "
    			+unicodeencoding.GetString(bText)+"\r\n";						
      }
      catch(Exception ex)
      {
       //Программа будет выводить этот текст
       textBox1.Text+="Читать текст со второго процесса от начала файла нельзя - файл заблокирован \r\n";
      }
      //Байт для чтения
      byte bText1;
      //Здесь прерывания не будет - начало чтения за последним блокированном байтом
      try
      {
       filestream1.Seek(2,SeekOrigin.End);						
       bText1=(byte)filestream1.ReadByte();								
       bText[0]=bText1;
       bText1=(byte)filestream1.ReadByte();								
       bText[1]=bText1;
       textBox1.Text+="5. Читать файл и от конца файла можно, там где не заблокировано \r\n";
       textBox1.Text+=" "+unicodeencoding.GetString(bText)+"\r\n";
      }
      catch(Exception ex)
      {
       textBox1.Text+="Читать файл и от конца файла нельзя - файл заблокирован \r\n";
      }
      //Cнимаем тормоз первого процесса а он снимет блокировку
      manualresetevent.Set();
     }
     //Убеждаемся что блокировка снята и все можно
     using(FileStream filestream1 = new FileStream(path, FileMode.OpenOrCreate, 
       FileAccess.ReadWrite, FileShare.ReadWrite))
     {
      //Записываем текст в файл
      filestream1.Write(unicodeencoding.GetBytes(sText),0, sTextLength);	
    			filestream1.Flush();
      textBox1.Text+="Записать в разблокированный поток теперь можно \r\n";
      filestream1.Seek(0,SeekOrigin.Begin);
      filestream1.Read(bText,0, sTextLength);								
      textBox1.Text+="6. Чтение из второго процесса - блокировка снята\r\n     "
    		+unicodeencoding.GetString(bText)+"\r\n";	
     }
    }					
    

    Вывод впрограммы в TextBox:

    1. Чтение из первого процесса - блокировки нет
         Эта первая строка из первого процесса, записанная в файл!
    2. Чтение из первого процесса - блокировка есть
        Эта первая строка из первого процесса, записанная в файл!
    Записать из первого процесса можно в заблокированный файл - прерывания нет
    3. Чтение из первого процесса измененного - блокировка есть
        Эта вторая строка из первого процесса, записанная в файл!
    Читать текст со второго процесса от начала файла нельзя - файл заблокирован 
    5. Читать файл и от конца файла можно, там где не заблокировано 
      //Записали нечитаемый  символ
    Блокировка файла снята 
    Процесс 1 завершен 
    Записать в разблокированный поток теперь можно 
    6. Чтение из второго процесса - блокировка снята
        Строка из второго процесса записанная в файл!					
    


В начало

Класс BufferedStream и его совместное использование с классом FileStream

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

Методы класса BufferedStream автоматически поддерживают буферизацию при совместном использовании класса с другими потоковыми классами оперирующими байтами. Для других типов данных используются классы BinaryReader и BinaryWriter.

Класс BufferedStream используется для реализации буфера для других потоков при операциях чтения и записи. Буфер создается в оперативной памяти (по умолчанию 4 Кб.). Одним из входных параметров конструктора выступает некоторый, уже созданный поток. Дальнейшие операции выполняются через методы класса BufferedStream, а все изменения в буфере сохраняются в потоке вызовом метода Flush.

Конструктор потока:

public BufferedStream(System.IO.Stream stream,int bufsize);

Параметры:

  • stream - поток;

  • bufsize - размер буфера в байтах.

BufferedStream наследует все методы класса Stream и не имеет отличных методов от класса FileStream. Все отличие использования потоков - скорость выполнения операций при обмене с файлами большими порциями данных.

Рассмотрим пример использования потока:

private void button1_Click(object sender, System.EventArgs e)
{  
 string path = "C:\\a.txt";
 //Создаем поток
 using(FileStream filestream=new FileStream(path,FileMode.Create,FileAccess.Write))
 {
  filestream.Seek(0,SeekOrigin.Begin);
  BufferedStream bufferedstream=new BufferedStream(filestream,512*8);  // буфер для потока
  string sString = "Текст, который будет записан в файл";				
  //Писать в файл будем в Windows кодировке по умолчанию (по русски)
  Byte[] bText = System.Text.Encoding.Default.GetBytes(sString);               				
  bufferedstream.Seek(0,SeekOrigin.Begin);
  while(bText.Length > bufferedstream.Position )
  {
   bufferedstream.WriteByte(bText[bufferedstream.Position]);	
  }
  bufferedstream.Close();
 }
 using(FileStream filestream = new FileStream (path, FileMode.Open , FileAccess.Read , FileShare.Read ))
 {
  filestream.Seek(0,SeekOrigin.Begin);
  using(BufferedStream bufferedstream = new BufferedStream (filestream,528*4))
  {
   char[] ch=new char[bufferedstream.Length];
   Byte[] bText = new Byte[bufferedstream.Length];
   while(bufferedstream.Length > bufferedstream.Position )
   {
    bText[bufferedstream.Position]=(byte)bufferedstream.ReadByte();
   } 
   textBox1.Text=System.Text.Encoding.Default.GetString(bText);
  } 	
}        


В начало

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

Как отмечалось, для чтения и записи примитивных типов данных предназначены два класса BinaryReader и BinaryWriter, которые являются прямыми наследниками класса Object.

Конструкторы классов инициализирующие экземпляр соответствующего класса:

public BinaryWriter(Stream output);
public BinaryReader(Stream iinput);
public BinaryWriter(Stream output,Encoding encoding);
public BinaryReader(Stream input, Encoding encoding);

Первые два конструктора инициализируют экземпляры классы для соответствующего потока с использованим UTF8Encoding (кодировка UTF8), третий и четвертый конструкторы позволяет выбрать кодировку данных потока.

Возможные исключения:

  • ArgumentException поток не поддерживает чтение или запись соответственно, или поток закрыт.

  • ArgumentNullException - нулевая ссылка на поток.

Пример создания экземпляров классов:

BinaryWriter binarywriter = new BinaryWriter(File.Open(path, FileMode.OpenOrCreate));
BinaryReader binaryreader = new BinaryReader(File.Open(path, 
                     FileMode.OpenOrCreate),System.Text.Encoding.Default);

Напомним, что некоторые из методов класса File (Open, Create) создают объекты типа FileStream или StreamWriter, так метод Open создает объект типа FileStream.


В начало

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

Класс BinaryWriter предназначен для выполнения записи примитивных типов данных в ассоциированный поток. Основной метод класса - Write() - являеися перегружаемым и может выполнять запись для различных типов данных в поток с которым ассоциирован BinaryWriter.

Отметим, что при рассмотрении методов и свойств класса BinaryWriter мы не будем приводить примеры - все примеры приведены ниже при рассмотрении класса BinaryReader по принципу - "что записали - то и прочитали".

Рассмотрим основные методы и свойства класса:

  • Метод закрытия потока:
       -Close() - метод закрывает текущий поток и освобождает все ресурсы, связанные с текущим потоком и файлом.

    Метод вызывает прерывание IOException если ошибка произошла при попытке закрыть поток.

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

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

    Метод может вызвать прерывания IOException - если указатель файла перемещен в недопустимое местоположение или ArgumentException - значение SeekOrigin недопустимо для данного местоположения указателя.

  • Основной метод класса:
       -Write(variable);
       -Write(byte[],int beginbyte,int endbyte);
       -Write(char[],int beginchar,int endchar)

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

    Переменная variable может иметь тип: bool, byte, byte[], char, char[], decimal, double, short, int, long, sbyte, float, string, ushort, uint. Последние две реализации пишут соответственно сектор массива с по указанное число байт или символов.

    Основные прерывания для методов: IOException - ошибка вода/вывода при записи, ObjectDisposedException - поток закрыт, ArgumentNullException - буфер для потока есть нулевая ссылка (при работе с массивами) и для записи секторов ArgumentException - при неверном задании индексов, ArgumentOutOfRangeException - индекс или индексы отрицательны.


    В начало

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

    • Метод Close() - закрывает текущий и основной потоки;

    • Метод PeekChar() - возвращает следующий символ из потока не перемещая указатель текущей позиции. Это дает возможность использовать данный метод для проверки наличия символов в потоке перед их прочтением. Возможное исключение IOEscaption при ошибках ввода вывода

    • Методы:
      -Read(byte[] bufer,int offset,int count);
      -Read(char[] bufer,int offset,int count);
      -Read();

      - читает count байт или символов или очередной символ с позиции index в указанный источник.

      Возможные исключения:

      • ArgumentException - длина массива минус смещение - меньше чем индекс.

      • ArgumentNullException - нулевая ссылка на массив.

      • ArgumentOutOfRangeException - смещение или индекс отрицательны.

      • IOException - поток закрыт.

      • NotSupportedException - поток не поддерживает чтение.

      • ObjectDisposedException - метод вызван после закрытия потока.

    • Методы:
      -ReadBoolean() - читает значение Boolean из потока и перемещает текущую позицию указателя на один байт.
      -ReadByte(); ReadSByte() - читают один байт из потока и перемещают текущую позицию указателя на один байт. Возвращаемое значение - байт интерпретируемый соответственно как число без и со знаком
      -ReadBytes(int count) - методы читают count байт из потока и перемещает текущую позицию указателя на count байт. Метод присваивает массиву значения прочитанных байт, например:

      byte[] bArray = binaryreader.ReadBytes(bArray.Length);
      

      -ReadChar() - читает символ из потока и перемещает текущую позицию указателя на число байт отведенное на один символ в данном Encoding.
      -ReadChars(int count); - читает count символов из потокаа, возвращает данные в символьной массив, и продвигает указатель на число байт отведенное на один символ в данном Encoding.

      char[] cArraybinaryreader.ReadChars(cArray.Length);
      

      Пример чтения текстового файла:

      private void button1_Click(object sender, System.EventArgs e)
      {
       string path = @"C:\a.txt";
       using(FileStream filestream=File.Open(path, FileMode.OpenOrCreate))
       {
        BinaryReader binaryreader = 
            new BinaryReader(filestream,System.Text.Encoding.Default);			
        char[] chText=binaryreader.ReadChars((int)filestream.Length);		
        textBox1.Text=new string(chText); 
       }
      }
      


      -ReadString() - возвращает очередную строку из потока.

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

      Пример использования:

      private void button1_Click(object sender, System.EventArgs e)
      {
       string path = @"C:\a.txt";
       string sText="";
       //Запись строк
       using(FileStream filestream=File.Open(path, FileMode.OpenOrCreate))
       {
        BinaryWriter binarywriter = new BinaryWriter(filestream);
        //Делаем длинную строку из символов W
        for(int i=0; i < 1024; i++)
        {
         sText+="W";
        }
        //Записываем строку
        binarywriter.Write(sText);
        //Делаем длинную строку из символов M
        sText="";
        for(int i=0; i < 512; i++)
        {
         sText+="M";
        }
        //Записываем строку
        binarywriter.Write(sText);
       }
       //Чтение строки
       using(FileStream filestream=File.Open(path, FileMode.OpenOrCreate))
       {
        BinaryReader binaryreader = 
      	new BinaryReader(filestream,System.Text.Encoding.Default);			
        filestream.Seek(0,SeekOrigin.Begin);
        if(binaryreader.PeekChar() != -1)
        {
         textBox1.Text+=binaryreader.ReadString();		 
        }
       }
      }
      


      -   ReadDecimal() - читает decimal значение числа из текущего потока и продвигает указатель на 16 байт. Напомним - decimal - 128-разрядный тип данных, имеющий по сравнению с типом данных с плавающей запятой большую точность и несколько меньший диапазон отображаемых чисел (от +/-1.0*10**-28 до +/-7.9*10**28).

      Пример использоывания:

      private void button1_Click(object sender, System.EventArgs e)
      {
       string path = @"C:\a.txt";
       //Записываем число decimal в файл
       using(FileStream filestream=File.Open(path, FileMode.OpenOrCreate))
       {
        BinaryWriter binarywriter = new BinaryWriter(filestream);
        //Число 1 типа decimal
        decimal dNum=10.5m;
        binarywriter.Write(dNum);
        //Число 2 типа decimal
        dNum=123.05m;
        binarywriter.Write(dNum);
        //Число 3 типа decimal
        dNum=1000.105m;
        binarywriter.Write(dNum);
       }
       //Читаем число 
       using(FileStream filestream=File.Open(path, FileMode.Open))
       {
        BinaryReader binaryreader = new BinaryReader(filestream);
        decimal dNum;
        filestream.Seek(16,SeekOrigin.Begin);
        textBox1.Text+=binaryreader.ReadDecimal().ToString()+" ";
        filestream.Seek(32,SeekOrigin.Begin);
        textBox1.Text+=binaryreader.ReadDecimal().ToString()+" ";
        filestream.Seek(0,SeekOrigin.Begin);
        textBox1.Text+=binaryreader.ReadDecimal().ToString()+" ";
       }
      }
      

      Результат в TextBox:

      123,05 1000,105 10,5
      


      -ReadDouble(); ReadSingle() - читают из потока 8 и 4 байта соответственно и преобразует их к числу с плавающей запятой и продвигает указатель на 8 или 4 байта.

      Модернизируем предыдущий пример заменив переменную на тип double (или float по желанию) и, изменив размеры сдвигов указателя при чтении с кратных 16 на кратные 8 (4). Результат в TextBox будет темже, что и в предыдущем примере.

      private void button1_Click(object sender, System.EventArgs e)
      {
       string path = @"C:\a.txt";
       //Объявляем переменную типа double
       double dbNum;
       using(FileStream filestream=File.Open(path, FileMode.OpenOrCreate))
       {
        BinaryWriter binarywriter = new BinaryWriter(filestream);
        dbNum=10.5;
        binarywriter.Write(dbNum);
        dbNum=123.05;
        binarywriter.Write(dbNum);
        dbNum=1000.105;
        binarywriter.Write(dbNum);
       }
       using(FileStream filestream=File.Open(path, FileMode.Open))
       {
        BinaryReader binaryreader = new BinaryReader(filestream);				
        filestream.Seek(8,SeekOrigin.Begin);
        textBox1.Text+=binaryreader.ReadDouble().ToString()+" ";
        filestream.Seek(16,SeekOrigin.Begin);
        textBox1.Text+=binaryreader.ReadDouble().ToString()+" ";
        filestream.Seek(0,SeekOrigin.Begin);
        textBox1.Text+=binaryreader.ReadDouble().ToString();
       }
      }
      


      -ReadInt16(); ReadInt32(); ReadInt64(); - читают из потока соответственно 2, 4, 8 байт и преобразует их к целому числу. Указатель в потоке также сдвигается на указанное число байт. Пример по использованию методов аналогичен описанным выше для типов переменных decimal и double, за исключением того, что переменным должно быть присвоено целое значение и учтены значения сдвига указателя на соответствующее число байт.


      -ReadUInt16(); ReadUInt32(); ReadInt64(); - читают из потока соответственно 2, 4, 8 байт и преобразует их к целому числу без знака. Указатель в потоке также сдвигается на указанное число байт. Пример по использованию методов может быть полностью аналогичкен приведенным выше для типов переменных decimal и double c учетом того, что переменным должно быть присвоено целое значение без знака и учтены значения сдвига указателя на соответствующее число байт.



    В начало

    Классы TextReader и TextWriter и их использование для чтения и записи последовательности символов


    В начало

    Классы TextReader и TextWriter и их методы

    Классы TextReader и TextWriter предназначены для чтения и записи последовательности символов и строк из/в ассоциированные с ними потоки. Наследники классов StreamReader и StreamWriter (классы для работы с последовательностью байт) и StringReader и StringWriter (классы для работы со строками).

    Работе со строками посвящен следующий параграф. В этом параграфе подробно рассматривается работа с последовательностью байт - с символами. Ниже рассматриваются методы классов TextReader и TextWriter, которые используются или во всех классах наследниках или только в одном из них, на что внимание будет обращено при рассмотрении классов наследников.

    Класс TextReader является абстрактным классом и предоставляет общие методы, которые наследуют классы StreamReader и StringReader для чтения символов или строк из потока - это:

    • Peek() - метод возвращает следующий символ из потока, но не перемещает указатель текущей позиции. Метод может вызвать прерывание IOExcaption только при ошибках ввода вывода. Обычно используется для проверки наличия следующего символа в потоке перед его чтением.

    • Read() - читает следущий символ из потока и передвигает указатель на один символ в соответствии с кодировкой cимволов в потоке, с которой ассоциирован класс наследник StreamReader.

    • Read(char[] buffer,int indexbegin,int count) - читает из потока в buffer count символов, с номера indexbegin и и возвращает реальное число прочитанных символов.

    • ReadBlock(char[] buffer,int indexbegin,int count) - считывает блок из count символов в buffer, начиная заполнение буфера с индекса indexbegin и возвращает реальное число прочитанных символов.

    • ReadLine() - считывает строку символов из потока;

    • ReadToEnd() - считывает все символы с текущей позиции до конца потока как одну строку.

    Методы чтения могут вызывать прерывания: OutOfMemoryException - недостаточно памяти в буфере для прочитанной строки, IOException - при ошибках ввода/вывода, ArgumentNullException - если буфер есть нулевая ссылка, ArgumentException - при выходе индекса или индекса плюс число читаемых символов за границу буфера.

    Класс TextWriter является абстрактным классом и предоставляет общие методы, которые наследуют классы StreamWriter и StringWriter для записи последовательности символов и строк в поток.

    Общие методы класса:

    • Close() - закрывает существующий поток TextWriter и освобождает все системные ресурсы, связанные с ним. После закрытия потока любые операции с ним вызывают исключения.

    • Flush() - очищает все буфера для данного потока и заставляет записать в поток все буферизованные данные;

    • Write(variable); - перегруженные версии метода позволяют записывать в поток любые базовые типы данных в текстовом формате.

      Переменная variable может иметь тип: bool, char, char[], decimal, double, int, long, float, string, uint, ulong, object. Указание в качестве представления переменной типа object означает, что метод позваляет записать текстовое представление объекта как текстового потока, например, применив к нему метод класса ToString.

    • Write(char[] buffer, int index, int count); - пишет сектор массива в текстовый поток начиная с символа index и число символов count.

    • Write(string format, object arg); Write(string format, object[] arg); Write(string format,object arg0,object arg1); Write(string format,object arg0,object arg1,object arg2); - методы позволяет писать в поток отформатированную строку к которой приведены объекты argX, используя строку формата format.

    • WriteLine(); WriteLine(variable);.... - метод полностью аналогичен и по перегружаемым реализациям и по используемым переменным методу Write (см. выше) и отличается от него лиш тем, что данные пишутся в виде текстовой строки, заканчивающейся стандартными разделителями строк. WriteLine без параметров пишет пустую строку.

    Методы Write и WriteLine могут вызывать прерывания: ObjectDisposedException - если TextWriter закрыт, IOException - при ошибках ввода/вывода,ArgumentNullException - если строка формата - нулевая ссылка, FormatException - если спецификация формата недопустима.


    В начало

    Класса StreamWriter

    По функциональным возможностям StreamReader и StreamWriter при их использование для чтения и записи последовательности символов близки к рассмотренным выше классам последовательного доступа. Рассмотрим их особенности (примеры кодов будут приведены в отдельном параграфе после рассмотрения класса StreamReader).

    Конструкторы класса StreamWriter:
    StreamWriter(string path);
    StreamWriter(Stream stream);
    StreamWriter(Stream path,Encoding encoding);
    StreamWriter(string stream,bool append);
    StreamWriter(Stream stream,Encoding encoding,int buffersize);
    StreamWriter(string path,bool append,Encoding encoding);
    StreamWriter(string path,bool append,Encoding encoding,int buffersize);

    Конструкторы инициализируют новый экземпляр класса StreamWriter для потока stream или файла по адресу patch, используя при отсутствии параметров значения по умолчанию для Encoding и размер буфера или заданные encoding и buffersize. Если файл существует, то при append=true информация дописывается в конец файла или при false файла перезаписывается - если файл не существует, конструктор создает новый файл.

    Основные свойства класса наследуются от класса TextWriter и рассмотрены выше. Среди свойств и методов собственно класса (не унаследованных от TextWriter) обратим внисание на:

    • Newline - метод позволяет записать в поток признак конца линии.

      FileInfo fileinfo = new FileInfo(path);
      StreamWriter streamwriter = fileinfo.CreateText();
      streamwriter.WriteLine("Строка 1");
      //Здесь будет пропущена строка
      streamwriter.Write(streamwriter.NewLine);
      streamwriter.WriteLine("Строка 3");
      
    • Encoding - свойство позволяет получить для потока значение Encoding.

      FileInfo fileinfo = new FileInfo(path);
      StreamWriter streamwriter = fileinfo.CreateText();
      textBox1.Text+=streamwriter.Encoding.ToString()+"\r\n";
      

      Пример выведет:

      System.Text.UTF8Encoding
      
    • FormatProvider - свойство позволяет получить для потока значение языковой среды. Зная значение свойства можно предусмотреть различные перегружаемые функции для различной языковой среды.

      FileInfo fileinfo = new FileInfo(path);
      StreamWriter streamwriter = fileinfo.CreateText();
      textBox1.Text+=streamwriter.FormatProvider.ToString()+"\r\n";
      

      Пример выведет для руской версии Windows:

      ru-RU
      
    • AutoFlush - свойство позволяет при значении true производить очистку буферов потока и запись данных в файл при каждом обращении к StreamWriter.


    В начало

    Класса StreamReader

    Конструкторы класса StreamReader:
    public StreamReader(string path);
    public StreamReader(Stream stream);
    public StreamReader(Stream stream,bool detectEncodingFromByteOrderMarks);
    public StreamReader(Stream stream,Encoding encoding);
    public StreamReader(string path,bool detectEncodingFromByteOrderMarks);
    public StreamReader(string path,Encoding encoding);
    public StreamReader(Stream stream,Encoding encoding,bool detectEncodingFromByteOrderMarks);
    public StreamReader(string path,Encoding encoding,bool detectEncodingFromByteOrderMarks);
    public StreamReader(Stream stream,Encoding encoding,bool detectEncodingFromByteOrderMarks,int buffersize);
    public StreamReader(string path,Encoding encoding,boo detectEncodingFromByteOrderMarksl,int buffersize);

    Конструкторы инициализируют новый экземпляр класса StreamReader для потока stream или файла по адресу patch, используя при отсутствии параметров значения по умолчанию для Encoding и размер буфера или заданные encoding и buffersize. Параметр detectEncodingFromByteOrderMarks при значении true позволяет обнаруживать encoding по первым трем байтам потока (для UTF-8, little-endian Unicode и big-endian Unicode).

    Основные методы StreamReader унаследованы им от класса TextReader и были рассмотрены выше. Из не унаследованных свойств и методов отметим:

    • CurrentEncoding - свойство позволяет получить для потока значение Encoding.

      StreamReader streamreader = File.OpenText(path);
      textBox1.Text+=streamreader.CurrentEncoding.ToString()+"\r\n";
      

      Пример выведет:

      System.Text.UTF8Encoding
      


    В начало

    Примеры использования классов StreamWriter и StreamReader

    Все примеры построены по принципу "что записано - то и прочитано". В примерах используется все то же решение, которое использовано для примеров этого раздела - единственное, что меняется - обработчик события нажатия кнопки 1.

    Пример 1. Конструкторы с одним параметром path и запись и чтение символов

    Используем простой конструктор для потоков. Чтобы не пользоваться методом Close() будем использовать конструкцию USING.

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

    private void button1_Click(object sender,System.EventArgs e)
    {
     string path = @"C:\a.txt";
     string sText="Текст который будет записан в файл а потом прочитан";
     using(TextWriter streamWriter = new StreamWriter(path))
     {
      streamWriter.Write(sText);
     }
     using(TextReader streamreader = new StreamReader(path))
     {
      //Текст "Текст который будет записан в файл а потом прочитан"
      //будет выведен в контрол TextBox
      textBox1.Text=streamreader.ReadToEnd();
     }
    }
    

    Пример 2. Конструкторы с одним параметром path и запись и чтение текстого представления переменных

    Используем вновь простой конструктор для потоков. Запишем в файл и прочитаем цифровую информацию.

    private void button1_Click(object sender,System.EventArgs e)
    {
     string path = @"C:\a.txt";
     using(TextWriter streamWriter = new StreamWriter(path))
     {
      for(int i=1; i < 3; i++)
      {
       streamwriter.Write(i*10/3);
       streamwriter.Write(streamwriter.NewLine);
       streamwriter.Write((uint)i*10/3);
       streamwriter.Write(streamwriter.NewLine);					
       streamwriter.Write((long)i*10/3);
       streamwriter.Write(streamwriter.NewLine);
       streamwriter.Write((ulong)i*10/3);					
       streamwriter.Write(streamwriter.NewLine);
       streamwriter.Write((decimal)i*10/3);
       streamwriter.Write(streamwriter.NewLine);
       streamwriter.Write((float)i*10/3);
       streamwriter.Write(streamwriter.NewLine);
       streamwriter.Write((double)i*10/3);
       streamwriter.Write(streamwriter.NewLine);									
      }
     }
     using(TextReader streamreader = new StreamReader(path))
     {
      textBox1.Text=streamreader.ReadToEnd();
     }
    }
    

    Результат и в контроле TextBox и в файле (если посмотреть файл любым текстовым редактором) цифры - причем в файле их текстовое представление (двоичное содержание файла - шестнадцатеричные коды (30-39):

    3
    3
    3
    3
    3,3333333333333333333333333333
    3,333333
    3,33333333333333
    6
    6
    6
    6
    6,6666666666666666666666666667
    6,666667
    6,66666666666667
    

    Пример 3. Запись, чтение символов при использовании вспомогательных потоков

    Как мы отметили ранее конструктор с одним параметром path прост, но не дает возможность определять длину потока, проводить позиционирование, и т.п. Частично эти трудности можно обойти выполняя все манипуляции со вспомогательным потоком и, затем, записывая в основной поток измененные данные. Так, в следующем примере используется как вспомогательный поток MemoryStream (мы его не рассматривали, но по методам и свойствам полностью аналогичен потоку FileStream, за исключением хранения информации не в файле, а в памяти). Его использование дает возможность позиционировать запись информации и манипулировать длиной потока, а следовательно и файла.

    private void button1_Click(object sender, System.EventArgs e)
    {
     string path = @"C:\a.txt";
     string sText="Текст который будет записан в файл";
     string sText1=" Новый текст";
     int length=sText.Length;			
     //Определяем длины текста, которые будут 
     //необходимы для определения длины потока
     UTF8Encoding utf8encoding=new UTF8Encoding();
     int iTextLength = utf8encoding.GetByteCount(sText);	
     int iTextLength1 = utf8encoding.GetByteCount(sText1);	
     using(MemoryStream memorystream = new MemoryStream())
     {
      //Устанавливаем длину потока MemoryStream
      memorystream.SetLength(iTextLength+iTextLength1);
      //Пишем в поток
      memorystream.Write(utf8encoding.GetBytes(sText),0,iTextLength);
      //Позиционируем
      memorystream.Seek(iTextLength,SeekOrigin.Begin);
      //Вновь пишем в поток
      memorystream.Write(utf8encoding.GetBytes(sText1),0,iTextLength1);
      using(StreamWriter streamwriter = new StreamWriter(path))				
      {
       //Переносим в файл
       streamwriter.Write(utf8encoding.GetChars(memorystream.ToArray()));
      }
     }
     //Чтение символов из  файла
     using(TextReader streamreader = new StreamReader(path))
     {
      textBox1.Text=streamreader.ReadToEnd();
     }
    }
    

    Результат:

    Текст который будет записан в файл Новый текст
    

    Теперь, слегка изменив код предыдущего примера (От коментария "Чтение символов из файла"), покажем как можно провести позиционирование при чтении. Здесь в качестве вспомогательного будем использовать поток FileStream:

    using(FileStream filestream = new FileStream(path,FileMode.Open))
    {
     using(TextReader streamreader = new StreamReader(filestream))
     {
      //Производим позиционирование в потоке, ассоциированном 
      //с файлом
      filestream.Seek(iTextLength,SeekOrigin.Begin);
      //Выводим часть текста
      textBox1.Text=streamreader.ReadToEnd();
     }
    }
    

    Результат:

     Новый текст
    

    Пример 4. Работа с классами StreamReder и StreamWriter без использования конструкторов

    Напомним, что многие методы работы с файлами возвращают при выполнении потоки типа StreamReder или StreamWriter. В примере, приведенном ниже, будет использован метод класса File CreateText, который, как мы знаем, создает или открывает файл для записи в кодировке UTF-8 и возвращает при успешном выполнении поток StreamWriter. Для создания потока StreamReader можно использовать метод класса File OpenText. Рассмотрим пример:

    private void button1_Click(object sender, System.EventArgs e)
    {
     string path = @"C:\a.txt";
     string sText="Текст который будет записан в файл";
     string sText1=" Новый текст";
     using(StreamWriter streamwriter = File.CreateText(path))
     {
      streamwriter.Write(sText+"\r\n"+sText1);
     }
     using(StreamReader streamreader = File.OpenText(path))
     {
      textBox1.Text=streamreader.ReadToEnd();
     }
    }
    

    Результат выполнения - данные в контроле TextBox:

    Текст который будет записан в файл
     Новый текст
    

    Аналогично можно использовать и класс FileInfo, однако, как нам уже известно, для этого необходимо вначале создать его экземпляр (класс не статический в отличии от класса File):

    FileInfo fileinfo = new FileInfo(path);
    StreamWriter streamwriter =fileinfo.CreateText();
    

    Далее полная аналогия с рассмотренным примером.

    Пример 5. Использование сложных конструкторов для StreamReder и StreamWriter

    Следующий пример позволяет не только писать в файл, но и сменить кодировку символов в файле с UTF8 на принятую по умолчанию (русская кодировка):

    private void button1_Click(object sender, System.EventArgs e)
    {
     string path = @"C:\a.txt";
     string sText="Текст который будет записан в файл";
     string sText1=" Новый текст";
     using(FileStream filestream = new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
     {
      using(StreamWriter streamwriter = new StreamWriter(filestream,System.Text.Encoding.Default))
      {
       streamwriter.Write(sText);
      }
     }
     using(FileStream filestream = new FileStream(path,FileMode.Open,FileAccess.Read))
     {
      //Чтение символов из  файла
      using(TextReader streamreader = new StreamReader(path,Encoding.Default))
      {
       textBox1.Text=streamreader.ReadToEnd();
      }
    }
    

    Пример 6. Использование других кодировок в StreamReder и StreamWriter

    Хотя мы здесь помимо задание кодировки UTF7 используем и параметр detectEncodingFromByteOrderMarks для StreamReder, но влияние его в данном примере игнорируется, так как для UTF7 он не используется. Размер буфера для малых текстовых файлов также не играет никакого значения.

    private void button1_Click(object sender, System.EventArgs e)
    {
     string path = @"C:\a.txt";
     string sText="Текст который будет записан в файл";
     using(FileStream filestream = new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
     {
      using(StreamWriter streamwriter = new StreamWriter(filestream,System.Text.Encoding.UTF7))
      {
       streamwriter.Write(sText);
      }
     }
     using(FileStream filestream = new FileStream(path,FileMode.Open,FileAccess.Read))
     {
      //Чтение символов из  файла
      using(TextReader streamreader = new StreamReader(filestream,System.Text.Encoding.UTF7,true,512))
      {
       textBox1.Text=streamreader.ReadToEnd();
      }
     }
    }
    


    В начало

    Классы TextReader и TextWriter и их использование для чтения и записи строк


    В начало

    Классы TextReader и TextWriter их методы и свойства были подробно рассмотрены выше. В этом параграфе рассматриваются их два класса потомка, которые содержат методы для работы со строками.


    В начало

    Класс StringWriter

    Класс StringWriter предназначен для записи строк формата класса StringBuilder (пространство имен System.Text). Напомним, что класс StringBuilder используется для создания строк, содержимое которых может быть модифицировано (с помощью методов класса Insert, Replace, Remove - модификация строк и свойство Chars модификация символов).

    Конструкторы класса:
    public StringWriter();
    public StringWriter(StringBuilder stringbuilder);
    public StringWriter(IFormatProvider formatprovider);
    public StringWriter(StringBuilder stringbuilder,IFormatProvider formatprovider);

    Конструкторы создают экземпляр класса StringWriter либо для записи произвольного объекта класса StringBuilder, либо для конкретного экземпляра класса StringBuilder при задании параметра stringbuilder и для конкретной языковой среды при задании параметра formatprovider.

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

    StringWriter stringwriter = 
      new StringWriter(new CultureInfo("de-DE"));      
    stringwriter.Write(DateTime.Now);
      textBox1.Text+=DateTime.Now.ToString()+"  "+stringwriter.ToString();
    //Ввод будет различен для конкретной языковой среды new CultureInfo("среда")
    //"ru-RU" 11.10.2004 13:47:13  11.10.2004 13:47:13
    //"en-US" 11.10.2004 13:47:30  10/11/2004 1:47:30 PM
    //"ar-DZ" 11.10.2004 13:47:54  11-10-2004 13:47:54
    //"de-DE" 11.10.2004 13:48:19  11.10.2004 13:48:19
    

    Использование конструктора без параметров:

    StringBuilder sbText= new StringBuilder("Текст который будет записан в StringWriter");
    StringBuilder sbText1= new StringBuilder("Текст который будет вставлен в  StringWriter");
    StringWriter stringwriter = new StringWriter();      
    stringwriter.Write(sbText);
    stringwriter.GetStringBuilder().Insert(0,sbText1);
    textBox1.Text+=stringwriter.ToString();
    

    Результат:

    Текст который будет вставлен в  StringWriter Текст который будет записан в StringWriter
    

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


    В начало

    Класс StringReader

    Класс StringReader служит для чтения строк и символов из строк.

    Конструктор класса
    public StringReader(string str);

    Конструктор создает новый экземпляр класса StringReader, на базе строки инициализации str.

    Пример использования конструктора:

    string sText="Строка 1"+Environment.NewLine+"Строка 2"+Environment.NewLine+
                 "Строка 3"+Environment.NewLine+"Строка 4"+Environment.NewLine;
     string strLine= null;
    StringReader stringreader = new StringReader(sText);
    while(true)
    {
     strLine = stringreader.ReadLine();
     if(strLine != null)
     {
      textBox1.Text+=strLine+"\r\n";
     }
     else
     {					
      break;
     }
    }
    

    Результат:

    Строка 1
    Строка 2
    Строка 3
    Строка 4
    


    В начало

    Классы StringWriter и StringReader и работа с файлами

    Напомним, что многие методы работы с файлами возвращают при выполнении потоки типа StreamReder или StreamWriter, но как видно и из рассмотрения конструкторов классов и классов для работы с файлами, StringWriter и StringReader не могут быть сопоставлены непосредственно с файлом. Следовательно необходимо использовать то, что работает с файлами.

    При рассмотрении метода Write класса TextWriter мы отмечали, что мктод может иметь в качестве параметра переменную типо object, а указание в качестве представления переменной типа object означает, что метод позваляет записать текстовое представление объекта. На этой идее и базируется использование StringWriter и StringReader при работе с файлами. В следующем примере мы сначала делаем с текстовыми строками все что требуется, используя класс StringWriter, а затем переносим всю необходимую информацию в файл, используя метод Write для StreamWriter. Для чтения, сначала создаем StreamReader с которым ассоциируем файл, создаем StringReader и переносим в него текст из потока, далее можем работать с текстом и выполнить нго вывод в контрол TextBox.

    private void button1_Click(object sender, System.EventArgs e)
    {
     StringBuilder sbText= new StringBuilder("Текст который будет записан в файл");
     StringBuilder sbText1= new StringBuilder("Текст который будет добавлен в файл");
     StringBuilder sbText2= new StringBuilder("Текст который будет вставлен в файл");
     //Создаем экземпляр класса StringWriter
     StringWriter stringwriter = new StringWriter();      
     //Проводим замену текста в StringBuilder
     sbText=sbText.Replace("будет","был");
     sbText1=sbText1.Replace("будет","был");
     sbText2=sbText2.Replace("будет","был");
     //Пишем и вставляем текст
     stringwriter.Write(sbText);
     stringwriter.Write(stringwriter.NewLine);
     stringwriter.Write(sbText1);			
     stringwriter.GetStringBuilder().Insert(sbText.Length+1,sbText2);
     string path = @"C:\a.txt";
     //Переносим класс StringWriter в файл
     using(TextWriter streamWriter = new StreamWriter(path,true,System.Text.Encoding.Default))
     {
      streamWriter.Write(stringwriter);
     }
     stringwriter.Close();
     using(TextReader streamreader = new StreamReader(path,System.Text.Encoding.Default))
     {
      //Создаем экземпляр класса StringReader на базе текста файла
      StringReader stringreader = new StringReader(streamreader.ReadToEnd());
      textBox1.Text+=stringreader.ReadLine()+"\r\n";
      //Проводим изменения в тексте и выводим в контрол
      textBox1.Text+=stringreader.ReadLine()+"\r\n";
      sbText=new StringBuilder(stringreader.ReadLine());
      sbText=sbText.Replace("Текст", "Кекс ",0,5);
      textBox1.Text+=sbText;
      stringreader.Close();
     }
    }
    

    Текст в файле (можно посмотреть любым текстовым редактором):

    Текст который был записан в файл
    Текст который был вставлен в файл
    Текст который был добавлен в файл
    

    Текст в контроле TextBox:

    Текст который был записан в файл
    Текст который был вставлен в файл
    Кекс  который был добавлен в файл
    

    На этом рассмотрение использования потоковых классов при работе с файлами пока заканчивается, но как говорилось в данном разделе, к работи с другими типами файлов нам еще предстоит вернуться. Конечно же это будет после рассмотрения ряда тем, которые прежде всего необходимы для того, чтобы начать работать на C# при решении наиболее общих задач программирования.

    Еcли Вы пришли с поискового сервера - посетите мою главную страничку

    На главной странице Вы найдете программы комплекса Veles - для тех кто готовится к экзаменам на право управления автомобилем или мотоциклом, программу NumberPhoto, созданную для работы с фото, сделанными цифровым фотоаппаратом, программу Bricks - игрушку для детей и взрослых, программу записную книжку, теоретический материал по программированию в среде Borland C++ builder, C# (C .Net).

    На главную страницу

    В начало страницы

    К началу раздела


  • Сайт управляется системой uCoz